Merge branch 'master' into test

This commit is contained in:
Sarah Vaupel 2025-03-13 15:11:54 +01:00
commit fc2aa31d77
240 changed files with 7083 additions and 4348 deletions

1
.gitignore vendored
View File

@ -5,6 +5,7 @@ node_modules/
assets/icons
assets/favicons
bin/
assets/fonts/
*.hi
*.o
*.sqlite3

View File

@ -2,881 +2,6 @@
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.
## [v27.4.59-test-h0.0.18](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-h0.0.17...v27.4.59-test-h0.0.18) (2025-02-19)
## [v27.4.59-test-h0.0.17](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-g0.0.17...v27.4.59-test-h0.0.17) (2025-02-19)
## [v27.4.59-test-g0.0.17](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-f0.0.17...v27.4.59-test-g0.0.17) (2025-02-18)
## [v27.4.59-test-f0.0.17](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-g0.0.16...v27.4.59-test-f0.0.17) (2025-02-17)
## [v27.4.59-test-g0.0.16](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-f0.0.16...v27.4.59-test-g0.0.16) (2025-02-16)
## [v27.4.59-test-f0.0.16](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-f0.0.15...v27.4.59-test-f0.0.16) (2025-02-16)
## [v27.4.59-test-f0.0.15](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-e0.0.15...v27.4.59-test-f0.0.15) (2025-02-15)
## [v27.4.59-test-e0.0.15](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-f0.0.14...v27.4.59-test-e0.0.15) (2025-02-14)
## [v27.4.59-test-f0.0.14](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-e0.0.14...v27.4.59-test-f0.0.14) (2025-02-14)
## [v27.4.59-test-e0.0.14](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-e0.0.13...v27.4.59-test-e0.0.14) (2025-02-13)
## [v27.4.59-test-e0.0.13](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-e0.0.12...v27.4.59-test-e0.0.13) (2025-02-12)
## [v27.4.59-test-e0.0.12](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-d0.0.12...v27.4.59-test-e0.0.12) (2025-02-12)
## [v27.4.59-test-d0.0.12](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-d0.0.11...v27.4.59-test-d0.0.12) (2025-02-11)
## [v27.4.59-test-d0.0.11](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-c0.0.11...v27.4.59-test-d0.0.11) (2025-02-11)
## [v27.4.59-test-c0.0.11](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-b0.0.11...v27.4.59-test-c0.0.11) (2025-02-11)
## [v27.4.59-test-b0.0.11](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-c0.0.10...v27.4.59-test-b0.0.11) (2025-02-11)
## [v27.4.59-test-c0.0.10](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-b0.0.10...v27.4.59-test-c0.0.10) (2025-02-11)
## [v27.4.59-test-b0.0.10](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-a0.0.10...v27.4.59-test-b0.0.10) (2025-02-11)
## [v27.4.59-test-a0.0.10](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-a0.0.9...v27.4.59-test-a0.0.10) (2025-02-11)
## [v27.4.59-test-a0.0.9](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-a0.0.8...v27.4.59-test-a0.0.9) (2025-02-10)
## [v27.4.59-test-a0.0.8](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-a0.0.7...v27.4.59-test-a0.0.8) (2025-02-10)
## [v27.4.59-test-a0.0.7](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-a0.0.6...v27.4.59-test-a0.0.7) (2025-02-10)
## [v27.4.59-test-a0.0.6](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-a0.0.5...v27.4.59-test-a0.0.6) (2025-02-08)
## [v27.4.59-test-a0.0.5](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-a0.0.4...v27.4.59-test-a0.0.5) (2025-02-07)
## [v27.4.59-test-a0.0.4](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-a0.0.3...v27.4.59-test-a0.0.4) (2025-02-07)
## [v27.4.59-test-a0.0.3](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-a0.0.2...v27.4.59-test-a0.0.3) (2025-02-06)
## [v27.4.59-test-a0.0.2](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-a0.0.1...v27.4.59-test-a0.0.2) (2025-02-05)
## [v27.4.59-test-a0.0.1](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-a0.0.0...v27.4.59-test-a0.0.1) (2025-02-05)
### Bug Fixes
* **ghci:** ghci works now as expected ([c3117db](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive/commit/c3117dbdcd1de9ef9f0751afa45018e2ebce2c42))
## [v27.4.59-test-a0.0.0](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59...v27.4.59-test-a0.0.0) (2024-10-25)
### Features
* **util script:** Util script for renaming of files added. ([caf8fec](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive/commit/caf8fec5acb94df16293bf9aa0cdab766f8829e8))
* **frontend:** load icons from svg files ([22781e1](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive/commit/22781e1565e890cf6c5b40973146b0334cb667aa))
### Bug Fixes
* **stack.yaml:** move to uniworx.de gitlab ([55484e6](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive/commit/55484e631b786ea3710d322282019baf5292c243))
* **utils/renamer:** Mehr outputs nur im verbose-Fall. ([ac30cb9](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive/commit/ac30cb9e6712d0ee3f204da4863d1e2509af8a76))
* **utils:** Verboseparameter -v hinzugefuegt; rekursives makedir; genauere Meldungen. ([1806d9f](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive/commit/1806d9f01fc4a0746d2f9df42ef1ee6827c7fa09))
* **Dockerfile:** change rights of source dir to env user ([e7a8183](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive/commit/e7a8183656ae419cfee2942543045c6fa6a9caa3))
* **Makefile:** add missing dependency on well-known for backend-builds ([a09dc59](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive/commit/a09dc59f260843f8815c382576bb5254d21104bf))
* **frontend:** fixed icon colour in table headers ([4c4571d](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive/commit/4c4571d2d0879e89f2572eba6015d34a7f4794c8))
* **doc:** minor haddock problems ([d4f8a6c](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive/commit/d4f8a6c77b2a4a4540935f7f0beca0d0605508c8))
## [28.1.1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/t28.1.0...t28.1.1) (2024-04-22)
## [28.1.0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/t28.0.10...t28.1.0) (2024-04-18)
### Features
* **middleware:** allow Cross Origin Resource Sharing (CORS) ([e1a25cd](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/e1a25cdd311c3d606404f7f94549875a2a15e2b3))
### Bug Fixes
* **auth:** use appsettings for azure tenant id; refactor azure lookup url methods ([7a510b3](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/7a510b315d62131e5fb47da8c1398144c57d4587))
## [28.0.10](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/t28.0.9...t28.0.10) (2024-04-18)
## [28.0.9](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/t28.0.8...t28.0.9) (2024-04-08)
## [28.0.8](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/t28.0.0...t28.0.8) (2024-04-06)
## [28.0.7](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/d28.0.10...d28.0.7) (2024-03-22)
## [28.0.6](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/d28.0.7...d28.0.6) (2024-01-13)
## [28.0.5](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/d28.0.4...d28.0.5) (2024-01-10)
## [28.0.4](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/d28.0.3...d28.0.4) (2024-01-09)
## [28.0.3](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/d28.0.2...d28.0.3) (2024-01-07)
## [28.0.2](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/d28.0.1...d28.0.2) (2024-01-07)
## [28.0.1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/d28.0.0...d28.0.1) (2024-01-07)
## 28.0.0 (2024-01-06)
## [28.0.0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/t27.4.49...t28.0.0) (2024-03-21)
### ⚠ BREAKING CHANGES
* remove applications and allocations
* **auth:** additional authorisation caching
* **jobs:** Job offloading
* **migration:** ManualMigration
* **html-field:** StoredMarkup
* **course:** AccessPredicates now take continuation
* **workflows:** digests now json encode via base64
Also improve efficiency of marking workflow files as referenced
* **files:** files now chunked
* split foundation
* **db:** transactions need to be retryable, now
* **dry-run:** runDBRead
* **course-participants:** CourseParticipantState
* **system-messages:** names of cookies & configuration changed
* **allocations:** influence of grades on allocation priority now
relative when priorities are ordinal
* major version bumps
* markdown based HTML input
* **exams:** ExamResult now contains ExamResultPassedGrade
* major navigation refactor
* **hide-columns:** StorageManager version numbers
* **frontend:** Major frontend refactor
* **sub-study-fields:** superStudyField
* Bumped esqueleto
* yesod >=1.6
* **exams:** examPartName no longer required
* **exams:** Introduces ExamPartNumbers
* **users:** Remove UserLecturer and UserAdmin
* **courses:** auditing for course registrations and deregistrations, more
tightly couple exam results, exam registration, and course registration (delete
them together now)
* **csv:** CsvColumnsExplained now required
* **exams:** examOccurrenceName
* **exams:** examStart and examPublishOccurrenceAssignments now optional
* **exams:** E.isInfixOf and E.hasInfix
* **standard-version:** Start of new versioning schema
### Features
* add user-system-function ([abc37ac](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/abc37aca9c2aa5eafe7eea9333886b43189d5591))
* **add-users:** add page-action to add users from TUsersR ([9c62c9e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/9c62c9ee8ca365ceea206c696d3596e46927391c))
* **add-users:** connect confirmation form with handler ([c013ae9](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c013ae9efcd7df812b26ae32ce04ba3da4e6aef4))
* **add-users:** correctly add users and reroute ([fecc752](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/fecc752d6ce3aa952efabdac2ff17064f4091050))
* **add-users:** more page-actions for convenience ([a882a3c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a882a3c0d03ffc57902cbec779621fb58617ed3b))
* additional exam functions on show page ([214e895](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/214e8951e49bada7081b35cdf4a570eba3890f87))
* additional general purpose caching tier (memcachedLocal) ([939ab37](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/939ab37588bb71b14b8a9f3ab58d7440f598faf9))
* admin interface to issue tokens ([738ab7b](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/738ab7b738bf2264d74023aa90fa23461b21ac2c))
* admin-crontab-r ([460c133](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/460c133aac316fb9317c5f08823e04c22eb63fe9))
* **admin-crontab:** export as json ([bbd4916](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/bbd4916f3a556ce4c05eb3b2b5268c9c072fdfdd))
* **admin-test:** download test ([daaeb09](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/daaeb09de82ec5758cba564cb4bc5db7948f5476))
* **admin-users:** allow adding users ([67f1201](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/67f120120f616377c8b5ab34b63941475195fcac))
* **admin-workflows:** allow uploading graph spec as file ([48208c9](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/48208c9105ba49feb784ea3143b610ed5b11b517))
* admins can efficiently generate many tokens for random users ([600bbe5](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/600bbe5d7e9051e4a4eac540b01ff358666ebc9c))
* **alert-icons:** add custom icons for alerts ([bc67500](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/bc675006d8c72a2994be832236036447ebe1efc1))
* **alerticons:** allow alerts to have custom icons ([d70a958](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d70a9585f093c0701adf724ffe84cbaf3f1a592d))
* **alerts js:** support custom icons in Alerts HTTP-Header ([8833cb5](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/8833cb5090738c351b8a47af558dfcb91040cf77))
* **allocation-list:** show numbers of avail. and applied-to courses ([a3f236c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a3f236cb5f174c82924255f438861b8bdb320f8b))
* **allocations:** add application form(s) ([ef625cd](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ef625cd901bddc1e770bdae82ffb420b434087c1))
* **allocations:** add courses to allocations ([14a9a45](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/14a9a4567491253cb220c51a458e029e6d75e00a))
* **allocations:** add info page for allocations ([689b85a](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/689b85ad0868087d5f5163f9020ba035a557fe82))
* **allocations:** add registration form ([c5b18fc](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c5b18fcfcf3b970039d2d72eab6d6f7e646d72ef))
* **allocations:** additional info and explanation for participants ([38949cf](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/38949cfe0d3957c261b79b448a7fd17c20af1d25))
* **allocations:** admin-interface registrations ([5e38f03](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/5e38f03a85992964d4c9cf5100c4c8a4a8762aaf)), closes [#677](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/677)
* **allocations:** allocation-course-accept-substitutes ([8abcd65](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/8abcd65edf2a1bf5b6de62103af7427fa7ed7db3))
* **allocations:** allow additional notifications ([cc20559](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/cc205596ae55826dcefe51ebaef3227ad72bb3d8))
* **allocations:** allow changing course capacity during allocation ([83e1c94](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/83e1c9418a0461baebd6da8e0d835738d611f188))
* **allocations:** auxilliaries for allocation-algo ([47bfd8d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/47bfd8d4ea3e5ef9f5270c08c70346cd29aa44aa))
* **allocations:** compute & accept allocations ([20ef95c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/20ef95c142e0026b9a8bbe63fb60209b285509c6))
* **allocations:** create & edit, list & download matching logs ([5320a4f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/5320a4fe98f26576e6b72a1411107f410333009a))
* **allocations:** create model for allocations ([82e3bf9](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/82e3bf95c49a024f3afdbca4ce4c2744dda3303f))
* **allocations:** csv-export new-assigned ([a4114a7](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a4114a79f1bfd968bb9d300f0c39400a8904ee7c))
* **allocations:** delete allocation-users ([6a1a64a](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/6a1a64a6113fcae3a654e472fabdf5a3f622f549))
* **allocations:** display new allocations in user table ([bb20062](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/bb20062d9f8fd7169b7dd6f7d14e82d244af83b2))
* **allocations:** display number of ratings and vetos to admins ([6da8ad3](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/6da8ad348182185f773ab08a10ff59a6a1e89b85))
* **allocations:** display participant counts to admins ([b79bac7](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b79bac777c6d349a626ea4efa6c43141b7f669d0))
* **allocations:** edit allocation-user and their applications ([4daf33a](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/4daf33a1a0c2c621e295aef50aae9e2dc5d5a7e8))
* **allocations:** explanations & introduce grade-ordinal-proportion ([ee2e504](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ee2e504ffad276fbb0adc189175d29adb3e31e03))
* **allocations:** fingerprints & ordinal ratings ([60603cb](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/60603cb6ec43738bdbd98b5a2620366b20bf98bf))
* **allocations:** highlight app's of users without alloc'-user ([300c378](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/300c3787867622a7fe3580b63cbbfba86a8b21f3))
* **allocations:** implement application interface ([4dcc82a](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/4dcc82a7709ae4e145a666d170286e3f9f939d41))
* **allocations:** improve accept ui and logging ([3422fd7](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/3422fd70a73bf005622f4e9b94caa503eb92f553))
* **allocations:** improve acceptance display ([cf03277](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/cf0327787417e025e21f96f824893f85e6fdad57))
* **allocations:** improve display ([26f8f39](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/26f8f392a96893bc3c97f1c2212a9c71f0a610f7))
* **allocations:** include study features in users table ([7f7d2c7](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/7f7d2c795767fd6fac1fa4a10a304e3e3d2280c3))
* **allocations:** link allocations from home ([c759364](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c759364ab1d43a4e796cd92a494cafb939dd2568))
* **allocations:** merge notifications ([9e9e53e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/9e9e53e76abb471309494e36fc23b5a8ec4a09a8))
* **allocations:** notification about finished allocation ([9323220](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/93232201f2b62b61ed6f543d84f6373a13bd1ca5))
* **allocations:** notifications ([6d52ed5](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/6d52ed5c4caba9e164cc30e2affada85c7ddcf7f))
* **allocations:** notify about new courses ([18921e0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/18921e06d1deeb41d705eabacc2d348bac76197f))
* **allocations:** prevent course (de)registrations ([94a1208](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/94a120808dad0bea3b74ecb17f17e7daad5cb3f1))
* **allocations:** properly save allocation-relevant course-deregs ([7a759b1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/7a759b192fff62bc8e7608f58f861f4c2e313534))
* **allocations:** prototype assignment-algorithm ([0fcf48c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/0fcf48ce666b4828a33592e234ad2265d7f22952))
* **allocations:** refine model for allocations ([069eb1e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/069eb1e0b7a1285ae925f95ae1be71befe65fc12))
* **allocations:** serve archive of all application files by course ([5e393c5](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/5e393c53c6a702a88e053e585c1d38cb5fea15bf))
* **allocations:** set up routes ([c2df01c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c2df01c2f710eef3ec8ba8bd0a745f393169832c))
* **allocations:** show & export priority ([7462e03](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/7462e03e7073e73d298ee98cb2403c7221c2ea6a))
* **allocations:** show bounds on assignments due to allocation ([91b249e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/91b249e58ba4d839bf3c9324548c4f44caa4be7b))
* **allocations:** show more information ([b7c54df](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b7c54df9132b3759b3957dec4f09f6250b2f4623))
* **allocations:** show staff descriptions ([b359468](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b35946859309fbb526043194c8620c5fc0844809))
* **allocations:** show table of all allocations ([d621e61](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d621e61b11b1ddb85ba3c2611a24b0c28fe841c2))
* **allocations:** show table of course applications ([f5da3be](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/f5da3bebba8d5ef6ee9b665c73eba0ea24dd50dd))
* **allocations:** switch to csprng ([3ea7371](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/3ea7371465194decf072bf038c6d05b4790b6520))
* **allocations:** table of allocation users ([2735d46](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/2735d465eb6b91b5c6373171f9f93f751be120e9))
* **allocations:** tooltips listing courses in users table ([6bca64c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/6bca64cf5f50f5c0ce7f43575930902e46dd1b1d))
* **allocations:** ui for adding applicants ([7b7f11e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/7b7f11e72853e11717c671d434397c707eff3b7f))
* **allocations:** upload of priorities ([a590f45](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a590f45cc150dfac5d786963dbec351ff53a5b63))
* allow editing of course applications outside of allocation ([e816a30](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/e816a30b353f6451f48c97cc9a315f9b3aebb3a5))
* allow examFinished before examEnd ([21bbb92](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/21bbb92d4c5bca8e175d2be97515e14f67ad696b))
* allow separating user generated content into separate domain ([707b41d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/707b41d4ec9fa92238eaeb4e77f32d8bd8052c46))
* **applicants:** disclose applicant emails & allow communication ([6711173](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/67111736875f22e636b8abdb48c869ad06909875))
* **applications-list:** add warning regarding features of study ([cdbe12c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/cdbe12c7268ee923cf0ed8c5609584a0f5193dc0))
* **async-table:** history api ([c348b7c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c348b7cb035b2c48a8d85e1fe394116bba45f36e)), closes [#426](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/426)
* **async-table:** no submit on locked inputs ([22b3780](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/22b3780efdabdc572d6c5a08b282bb65448bf3f1))
* **audit:** automatic transaction log truncation ([248482b](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/248482b1bb791023fa7226e303a143996192d9c8))
* **audit:** introduce id-based format ([f602b79](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/f602b79e7a72c2dced293d5218a4f7bea98c610c))
* **audit:** take IP from header ([fb027de](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/fb027dee588d709662556874ba22af44af2183bd))
* **authorisation:** cookie-active-auth-tags ([0d372c6](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/0d372c636a735b4003448ab2518f6354b08ca042))
* **auth:** record student ldap role ([50455e6](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/50455e68a1e89e16a5905b976a09d265afa08bba))
* **auth:** user independent authorisation caching ([63f0d3c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/63f0d3c37ad4a02a5cbdf76398d4a9c74a0a0b59))
* automatically sync system functions from ldap ([297ff4f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/297ff4f02591339dda7f3270cc9cd332e18febb7))
* **avs:** add extraction functions for avs datatypes and tests ([f8afca0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/f8afca0598d03073e4200ba9c7946eec2b509d04))
* **avs:** add page-action and form handler for registering avs participants ([747d619](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/747d6198c4efdafab009012dc46ed65b02303a38))
* **avs:** add SetRampDrivingLicence and InfoRampDrivingLicence to AVS interface ([a1272e3](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a1272e38b72d146b881492341a86e1fc544ab0ff))
* **avs:** disable certificate validation for avs api ([66dd1a8](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/66dd1a8b70468a51aca0eba82369833acc8dcb3d))
* **avs:** register course participants for day groups per default ([64d3ceb](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/64d3ceb56d4a7ce09d7760c7452f48e12b182070))
* better explain behaviour of submittorForm ([b973495](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b9734953cfb0a63922e52b828c3b47cd32d2834e))
* **bot-mitigations:** only logged in table sorting ([fb6ae08](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/fb6ae089c63174edc1d84512ea35378ab8cd0e0e))
* bump changelog ([3bd7520](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/3bd75200874c812afc425d9652318b57336c31fb))
* bump changelog ([bc674af](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/bc674af936550d518e1d427ca86205a3a1e8c5d5))
* **caching:** aggressively cache nav items ([b9b0909](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b9b090992fd83fa4db1c408727d4c8c582b447e4))
* **caching:** introduce cache prewarming ([8d1f216](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/8d1f216b5b6ee2a59c3fb80f5dd4a701d9dad5ef))
* **changelog:** bump ([3d1636f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/3d1636ffe8b1b24b5f6e4a679bb1fbcdf9de5fa6))
* **changelog:** bump ([a684b90](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a684b90e5ec0545c571b72cab6336dd07f1e42aa))
* **changelog:** implement changelog like faq ([d9d353f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d9d353fcb7652c46a15016b5d2f400162c8271ef))
* **changelog:** prettify date formatting ([2b3aef7](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/2b3aef7a490ca2fbabd474069d3406ebb9403e4b))
* **communication:** send test emails ([d90da85](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d90da85df353da63794eeb95b79b87f485bb6908))
* **config:** improve configurability of VerpMode ([a7c3fe7](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a7c3fe76f23f9bd5ba15adcb8a469faf53ad3769))
* **copyright:** add english translation ([dbb0a57](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/dbb0a5708613d350843247b7ed55a96992db9772))
* **correction-interface:** wire up ECorrectR ([d8801a3](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d8801a3435088648cc490f71ec08eba91c4a68c3))
* **correction-interface:** wire up ECorrectR ([df66c9b](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/df66c9b58dd1ea6119d428470d2d089edd70e2d0))
* **correction:** allow lecturers to set corrector ([f74581c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/f74581c35648788a39d46c5a72acda4f2c2fa7b9)), closes [#414](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/414)
* **corrections assignment:** add convenience to table header ([56c2fcc](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/56c2fccb84ff71163ccc22291cf42c0cea88b2de))
* **corrections-grade:** additional column for sheetType ([4cb2d4f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/4cb2d4f07ddc0d873601ae43636a76adfd5a481a))
* **corrections-grade:** basic filter UI with pseudonyms ([d03fd4b](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d03fd4bee6d645624c30297907c91bf4697aa2f8))
* **corrections-grade:** sorting by sheetType ([702fb1d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/702fb1dfdb0bc9d9124d824efef56620b6666d33))
* **corrections-grade:** working additional filters ([c4eb2c0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c4eb2c0f04adc6c7193b602b6bfa570c1ba3483b))
* **corrections-r:** allow csv exporting one line per submittor ([7aadb66](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/7aadb6662bc8db76436f8d41ded7156acb98418e))
* **corrections-r:** authorship statement state ([51522ef](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/51522efc7c9915115e0d8791320a03e35d2933c8))
* **corrections-r:** csv export ([2a6248e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/2a6248e3d5d4f4de5f1c7d6c6bcf092dc9873a2e)), closes [#705](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/705)
* **corrections-r:** filter/sort by pseudonym ([153af8c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/153af8c6b4042430bb4bc120fa5c24a5d114e4c1))
* **corrections-r:** json export ([fe8e4bb](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/fe8e4bbd4f6a8b1b1c54808ebc96ee675a078648))
* **corrections:** added missing titles; small message fixes ([018082e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/018082ec4a8d1fb90fb5fa874a9b971f6162716b))
* **corrections:** better highlight corrected files ([46ce477](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/46ce477235fa488986728a7f42ec6a402cd01a98)), closes [#602](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/602)
* **corrections:** non-anonymous download w/ registered groups ([9032f80](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/9032f80f59532a0d26a1d67de50765dbb1c2e0e0))
* **corrections:** override rating_done & documentation ([bbbfa94](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/bbbfa946e1e2bd3cce6d30cffc672460ec5125bf)), closes [#525](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/525) [#274](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/274)
* **corrections:** submission filter ([38dbfe7](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/38dbfe73b25ef9cce15b4fdd7a529bb2a3abd9c7))
* **corrector-assignment:** show load/submission percentages ([228cd50](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/228cd507498a74f92978c2c9082d91348e68c564))
* **course admin:** application restore ([cb4ed8d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/cb4ed8d9887e521f47689c118baf439846cd4514))
* **course admin:** done ([15689c5](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/15689c597ef407583b01dabc9f7631e9dc90b009))
* **course admin:** no new-line ([0a6a174](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/0a6a1749d351e626383e513293af280f78552009))
* course applications study features ([44eeffc](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/44eeffcc70a8b4c119e1a88a9ef01c687fe2e10a))
* **course enrolement:** show proper icons in alerts ([b2b3895](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b2b3895aa97d19580987d4b7f845798d6603c44a))
* **course material:** auto vorschläge für materialtype ([decdda3](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/decdda359d16cce429a7e7a07d4674840e5fe6af))
* **course material:** first two filters ([90e4a62](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/90e4a620f0c1671ff332db1910c176e58ccbac06))
* **course material:** materialDescription in progress ([89e9887](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/89e9887fe1112cbc21517e4b501ead33f5a969ba))
* **course material:** materialdescription search implemented ([3a9622d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/3a9622dfb8474d9f3764f5870197e317a96d9de3))
* **course material:** merge-request suggestions ([dc5fc3f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/dc5fc3f710363f0644c43866505e32095b41ce92))
* **course material:** runDB für cid nur einmal ([c09acbb](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c09acbbf8a7b95176b3d52449b3b9d26e315ccd6))
* **course material:** small empty-bug fixed ([d8b1f97](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d8b1f9788c74ea5d7dc4f1f45432649d9601106a))
* **course-applications:** automatic acceptance of direct applicants ([620950d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/620950df83e3dc4d1f0050af4bb207d25883800e))
* **course-applications:** csv transport ([cf0ec1a](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/cf0ec1aec4267b99cf549b8ae5a0cd1762c45884))
* **course-comm:** recipient categories for sheets and exams ([2fd060d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/2fd060d55b7f027fa731dcb5b7d706e71d9ba413))
* **course-communication:** one recipient group per tutorial ([99f23f2](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/99f23f25582429e276f513f58677fe6676657ac3)), closes [#428](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/428)
* **course-edit:** warn about long shorthands ([80cb16a](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/80cb16a40f49564ad98395e4e0d16f405103a9d2))
* **course-events:** add HideColumns for course events ([1138f9e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/1138f9e32712eb879f76792a68a870e7a48c4b90))
* **course-events:** add optional note to course events ([6ad8f2e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/6ad8f2ee290d5b9166b07ba3be329483a769e097))
* **course-events:** course event note text -> html ([c8904d1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c8904d10b679e77bd7d4adaf80cd37bb3288275f))
* **course-events:** hide note column if there are no notes to display ([1ac7f4e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/1ac7f4e8811d9272a10aa28420b4e3fb0c976009))
* **course-events:** show notes in course events table ([b2c4125](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b2c4125ca3f76e65aa4eb4390e6d1e7013d1eb0d))
* **course-list:** filter by allocation ([de39686](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/de39686d89f6ec410bc50eaca058082dc727547d)), closes [#715](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/715)
* course-participant-lists ([88dd5a9](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/88dd5a90b91be365878f9424a3ab304c3ae7c339))
* **course-participants:** course-deregister-no-show ([bf64eaf](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/bf64eafd0877a686e3129f1da3ac352fda90e5d0)), closes [#499](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/499)
* **course-participants:** csv export exercise sheets ([06f47c5](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/06f47c59b434f1d97afc8afbcb1d47737a85c1d5))
* **course-participants:** csv export first name/surname separately ([1036926](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/1036926470792bf3409ba3a224886d48b7e1d314))
* **course-participants:** introduce CourseParticipantState ([d5b65a1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d5b65a1b06c6fdad2df537e270cba06e967f7ef7)), closes [#499](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/499) [#371](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/371)
* **course-participants:** show exercise sheets (first cornice) ([26cc8e4](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/26cc8e4b53261c64e2f23ab17a8f6ddcbb6fcd63))
* **course-registration:** allow independent course application ([a00698e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a00698e99e915fd9771d236ed5796bdfcad5b5c7))
* **course-show:** show "not registered" ([96e1a30](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/96e1a30eb64c8f18094ab5f3149bb675e9b8f14c))
* **course-show:** show allocation name ([3c80235](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/3c8023569bb3d8ce91ab6626b5a93763532a0d10))
* **course-teaser-css:** removed description label ([a25efb3](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a25efb3be4743f8667b106d4b00399a33d1ef605))
* **course-teaser-filter:** filter for lecturers ([e96e17f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/e96e17ff9f6701263e189fac6c6718e0a7ea0971))
* **course-teaser-filter:** working filters for semester and institute ([3b419b3](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/3b419b336670f94a2f5f024ded4abdb9c7d977fa))
* **course-teaser:** checkbox field for open registration filter ([e4f150d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/e4f150d0d536c36294abe6df27ba56a7ca5c16a9))
* **course-teaser:** display sorting "pills" for course teasers ([d964e1f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d964e1f705bb2fd9f9f4a220c6871f829b7b6ca3))
* **course-teaser:** filter by open registration ([c2c12b9](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c2c12b9643e81efd5f16e18e4fca4dcbd581db11))
* **course-teaser:** final version of course-teaser for course list ([66b97d6](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/66b97d6729c4feb94b0be50da43fcd3aa6c8c488))
* **course-teaser:** hide lecturer entry if empty ([f7fb3c1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/f7fb3c12198e29b653f93e9d9ef2f6fce23b1b85))
* **course-teaser:** incomplete course teaser for course list ([9a97925](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/9a9792578d98453ba0928d504ebbffd9ae7ccfda))
* **course-teaser:** moved course teaser functionality to util ([c99a3c7](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c99a3c7009fd44f15248b8b71abe07b5ba763c64))
* **course-teaser:** no display of chevron without description ([5c88c13](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/5c88c13cf835425a7ecbd9a0ed054b1ed5c67a12))
* **course-teaser:** no page reload on sorting ([68b8d24](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/68b8d2468175509533fc86ab4458268baa30ecb3))
* **course-teaser:** only true lecturers without assistants ([7926f29](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/7926f29da1565ce27600115d0030246cb1bf4ba0))
* **course-teaser:** redirecting to course/ ([aa20389](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/aa20389e05bbf683e0b998444f4b52af3e52327a))
* **course-teaser:** reintroduced courseId and course-teaser.julius ([3b6e700](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/3b6e700531205b8b8a3e9b319c4f7a9c6868bc95))
* **course-teaser:** show openCourses also to logged in users ([8cca548](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/8cca54897036e5959d8c06497f5a28bbbea888ed))
* **course-teaser:** unpolished version of course-teaser for course list ([ea5d54b](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ea5d54b2135820820aa24adab2807cb1bd03b8ec))
* **course-teaser:** working link to course pages ([8a49979](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/8a49979ecc26a54d3c2aff7056136f920abe13d8))
* **course-user:** authorisation checks ([d15792c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d15792cd7da8ce8038472af1afa721d4e99349dc))
* **course-user:** i18n ([da629a8](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/da629a81d28aeec37d53ef9a21ed959c73dd7e67))
* **course-user:** major improvements ([ced6ef2](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ced6ef287451cc59ef32f5454fc23e5cbf0f70eb)), closes [#126](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/126)
* **course-users-table:** json export ([6f291b2](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/6f291b2e6893554193732b059758794fe2b7fa51))
* **course-users:** allow for exam registration on CUsersR ([b8acc9b](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b8acc9b5da701bd0d17d9578ee2c9d4e835fc2d7))
* **course-users:** allow registering tutorial users manually ([d507d9b](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d507d9bbdea035d2d83050e5a0bbf61a73ae3161))
* **course-users:** exams in dbtable and csv ([c23becc](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c23becceb1ef994a39204ab3df083f1bbc857c27))
* **course-users:** filter by exam registrations ([1d7d0ab](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/1d7d0ab55422670d53425061961faa27829fba7c))
* **course-users:** fuse avs register form with CAddUserR ([4a00907](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/4a00907bdad26208329080ad87fe52daa33775ff))
* **course-users:** include tutorial in csv-export ([1d5ddd1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/1d5ddd102ce49c83ab0b45cf2be590f76ca0f0d0))
* **course-users:** match filter titles with column titles ([ecd7bec](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ecd7bec9aa36591854ee42e3feba3d8186f872b7))
* **course-users:** register avs-upserted users ([cba73bf](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/cba73bf2ca6825e9d4d00e51440354aba4cf57f0))
* **course-users:** register exam action with optional occurrence ([34ad1df](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/34ad1dfae2d265c2dd63325b2c742777a58eb699))
* **course-users:** set new tutorials to Schulung ([69de448](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/69de44893c9e37a809cde350d404f60a14e5052b))
* **course-visibility:** account for visibility in routes ([cb0bf15](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/cb0bf151212fd4ecba0eda7b1ef69a640fd6d35b))
* **course-visibility:** account for visibility on AllocationListR ([4185742](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/4185742f380f7625cac2bbc8df5952157ec0ba63))
* **course-visibility:** account for visibility on AShowR ([df7a784](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/df7a784a9daebbfb5ca61606cd38352662824131))
* **course-visibility:** account for visibility on TShowR ([0ff07a5](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/0ff07a5fad5504bff5adfdce278a6256f6bc8711))
* **course-visibility:** add invisible icon to CShowR title ([6c0adde](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/6c0adde5db117e6ad12167ebbb05a948e5c857c9))
* **course-visibility:** add visibleFrom,visibleTo ([222d566](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/222d566bdaa84382b24299d6e9179eb2ebb09564))
* **course-visibility:** allow access for exam correctors ([dfa70ee](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/dfa70ee7fea2020065699e2d4f0608195a1a0228))
* **course-visibility:** display icon in course list for lecturers ([17dbccf](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/17dbccf2a343cf1571ef0aaf07d6064bf3a2a216))
* **course-visibility:** error on visibleFrom > visibleTo ([9494019](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/94940196949014d6a99cb183514e855c37575fa5))
* **course-visibility:** hide invisible courses from favourites + icon ([d86fed7](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d86fed7a32badfe75ef124145e1c59086771c164))
* **course-visibility:** more precise description on CShowR ([6fbb2ea](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/6fbb2eabf1c90d3ab4e321773506ff8aebbb761d))
* **course-visibility:** no invisible courses in course list ([24f1289](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/24f12896e084e9180800a0080077d90005801642))
* **course-visibility:** now as default visibleFrom for new courses ([7bdf8ca](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/7bdf8cac8865743752401ee561355df466407223))
* **course-visibility:** redirect to NewsR after deregister (WIP!) ([183aa8d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/183aa8d2227091054f8409bd81f40031a8c2066d))
* **course-visibility:** reorder course form ([7af82bc](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/7af82bcb67d0ec6ae33aa82067b3d1f4de0d74de))
* **course-visibility:** rework visibility check for ZA courses ([a16eb1a](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a16eb1ab91747e19f61fd1dfaa5ae7db5800bad0))
* **course-visibility:** warn on deregister from invisible course ([16ad72d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/16ad72d87638a195afbfbc390f23182dc705f7fd))
* **course-visibility:** warn on invisibility during registration ([23aca1c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/23aca1caa43f24b38aa22d3cac3f2289b1cbe8f3))
* **course:** additional crosslinking ([5eaba78](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/5eaba7830f052fac42a0ce387619a09cefea48f8))
* **course:** allow csv-export of all features-of-study ([e60f1b2](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/e60f1b2bfcc7c914d202c30e9a5f155d28fae20a))
* **course:** associate qualifications with courses ([ffaaf9c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ffaaf9c86d5caa7eaec2d2bcd06bc6963310a7eb))
* **course:** csv export of course participants ([9a28dc8](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/9a28dc851cd4d8023ca3638b325b6b9575974a89))
* **course:** introduce CourseNews ([aa93b75](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/aa93b75e00cb28d3092c0ee4b538e21e543eb7d4))
* **courses:** add NotificationCourseRegistered ([3750da8](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/3750da81dc27fb104d4ad3765921af2e08915c4d))
* **courses:** course events ([fa7f771](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/fa7f7712f77a9e801a2c9fa040b1d97d0ae1f8bd))
* **course:** show direct registration dates ([8f284ac](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/8f284acde8229144f54d63464168b7b82f87c69e))
* **courses:** rework couse registration ([79d4ae2](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/79d4ae20ee7aaebf9b55005730383fe46ff84e12))
* **course:** warning if re-registration is not possible ([4451cee](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/4451ceedf7bde0da7f3bb4c0818b79d7c5df1cbd)), closes [#646](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/646)
* **crontab:** cronjob for pruning expired invitations ([a9c5276](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a9c527621ec17287b119617573e22ca918d20d9d))
* **csv import:** add explanation text ([6d0a4c1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/6d0a4c156bd8c792a9a01f05f6a41fe631da5517))
* **csv-export:** .xlsx ([5c51394](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/5c513946c15ed215f6958be1c7a435f03314f115))
* **csv-import:** automagically determine csv delimiters ([3555322](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/3555322f2a14ebdaeeab557d7c885e1179f0a90a))
* **csv:** add column explanations ([c8dca94](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c8dca945cfac12453bcc74bdfea321d7b4cb3053))
* **csv:** allow customisation of csv-export-options ([95ceedd](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/95ceeddc83ff79dad6f2dc494015e85f2996c40d))
* **csv:** don't limit number of exported rows ([e62d7a3](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/e62d7a34e68b79ae52450dcd9e1c5814933d33d1))
* **csv:** encoding ([81415e1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/81415e1afb02126a40bb1979ad4c039fd9cccb58))
* **csv:** export example data & improve zoned-time parsing ([49d9ab9](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/49d9ab9dba70423431e6e68892825c8e29309739))
* **csv:** finish implementing csv import ([e35fed6](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/e35fed6b85fd5734834e4a4590276c6d3df34f83))
* **csv:** implement csv import ([996bc2a](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/996bc2ac27bf8fccadcd5d30876dbd3263963cc1))
* **csv:** introduce csv export ([631bbef](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/631bbef0b8d202e3127de602ad1b5f30b896cd0a))
* **data-protection:** data protection statement contd ([c3c533f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c3c533f6a89eeb387abbd4ca17b56c0024c64b5d))
* **data-prot:** extend info on data saved ([2599e86](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/2599e86a59ccb58d0f18295475fd14fe95fe83f3))
* **datepicker:** add option to change the position of the datepicker ([85f46ef](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/85f46ef23075104346aba33861f8c170757a3c09))
* **datepicker:** also parse manual input in internal format ([8a3ac72](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/8a3ac72cbeffd467d3a64832621dae7666005a6f))
* **datepicker:** close datepicker on click outside ([88a6b85](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/88a6b85a7e83896beefa477b244b44c348a012f6))
* **datepicker:** close datepicker on escape keydown ([0e5707a](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/0e5707ac9fe69e80240b22c4f727170766cb7bea))
* **datepicker:** currently broken version using tail.datetime instead ([4282554](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/4282554d82d36c20f4887fdba8396a708dd794e9))
* **datepicker:** define instance collection singleton ([f5636b8](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/f5636b81d157f1b5df2bc4053aaf0efc3d1ba47a))
* **datepicker:** display datepicker on the right ([cbb7e95](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/cbb7e952769ef5208103766f4b32217819deed8e))
* **datepicker:** do not replace value if input is no valid date ([ecab0ac](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ecab0ac93ca28233457297e285f1a431232f8ace))
* **datepicker:** format according to input type; position datepicker ([db345ee](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/db345eed55a049b540f30802e77db33705349945))
* **datepicker:** format any dates before submission ([1eccb0e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/1eccb0ee4aba99a4ea05c15e6a898e44cc75b207))
* **datepicker:** format time on submit ([9f8749c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/9f8749c4cee73b43eb58d34eb18051f3b1753d31))
* **datepicker:** formatting dates for mass-inputs ([b9fd4d7](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b9fd4d7d285eb3779cff79de69dd3ebf7481c51a))
* **datepicker:** helper functions and updated tail.datetime fork ([2512d69](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/2512d69e675c31192c6de96f14d07306e82ff64c))
* **datepicker:** more sane datetime config ([5a44263](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/5a4426300abe83de223f6180039ced92722d2fb7))
* **datepicker:** new approach stub for formatting dates in formdata ([9ea7b2e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/9ea7b2e3f7db4111acf914354968824161ddd86c))
* **datepicker:** only update datepicker date if date is valid ([d857af3](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d857af3812f3810628c4837a8c7a74e8cd4bd1a9))
* **datepicker:** switch to tail.datetime fork to fix time selection ([863971f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/863971fbde19a39bd7fc2a16457f03463a41fdb0))
* **datepicker:** update dependencies ([427ffbf](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/427ffbf0d8acecc71446dd0250b2f6fc3ff20da3))
* **db:** automatic retry of database transactions upon system error ([e7a5162](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/e7a5162ec9560a20eaffdc24f143ab765f3f0238))
* **db:** optionally disable some db connection pooling ([35ac503](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/35ac503bf971ace21c49646aa15e8b94b7a3e823))
* **db:** provide our own implementation of connection pooling ([50fdcb4](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/50fdcb4540e6bfbc8da9ed10ed06d6f6ce443cf9))
* **dbtable:** add support for Cornice ([fdeb251](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/fdeb2514c0faae19604b578358806aeb4677a95e))
* **dbtable:** extra representations ([2c0fc63](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/2c0fc63be1de02e8acffbc6a9c5ee83b061c5825))
* **db:** track source of database accesses ([23ff9d9](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/23ff9d9222d7a75b2931827a6cc0335aafe753a1))
* **default-layout:** save handler ident to main content ([ba846be](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ba846be5aa99278ca00b64150165ccb825eb9ba0))
* demand authorship statements ([34b3e6a](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/34b3e6ae21b38a5b8389deade5deeb77b0981ead))
* **development:** add commitlint to ensure proper commit msgs ([dd528c1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/dd528c13aa28ecf882ee311aa2a22389b24c9585))
* **development:** add standard-version for automatic changelog generation ([c495ef5](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c495ef577231ac0db6b8c467cf4b0a69c3cf961c))
* **docker:** wrap within tini ([c86f36b](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c86f36b2f67a014352b043397c32b6d8ca1642fe))
* document CourseEvents ([db224cf](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/db224cf58e6025c89174bfa1eab09e03fbec2f07))
* don't redirect monitoring routes & crontab tokens ([3a106d1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/3a106d1ee5998ddeea852b3b0398c2f330664a63))
* **dry-run:** implement dry-run ([002775e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/002775e19296bd75cf016146e594df2f6101948b))
* **eecorrectr:** add handlers and navigation ([be2eb3c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/be2eb3c38d6539056456978b37c47c049d1cd683))
* **eecorrectr:** basic handler structure (WIP) ([de02895](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/de02895ed0ddc7ed119b76fded1d3eaec24448ba))
* **eecorrectr:** more appropriate error messages ([3b4c7fe](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/3b4c7fed3658cf3abe731fe45f24fe9c18b52b54))
* **eeusers:** fix form & finish implementation ([7d3e9a3](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/7d3e9a3de359a2775e2a2941cd807dd51dc07f0f))
* **eeusersr:** audit external exam result delete ([baa3fd8](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/baa3fd82e1642e5527b94cf71bc6e96dc0f81455))
* **eeusersr:** audit external exam result result and occurrence edits ([ed3f761](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ed3f761b24f98527ca0b26b09db7a75f1c98142e))
* **eeusersr:** audit external exam result result edit ([0d54757](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/0d54757d16bebadaeceeb0947df65f15d86ac3e0))
* **eeusersr:** more on actions, TODO audit ([d4b784a](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d4b784afba450019d34a7c70eff7992dbad3b9bf))
* **eeusersr:** stubs for new actions ([4d48730](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/4d48730abd7b1c4d4d9faab08734289fc6d6afb8))
* ensure cached study feature relevance is up to date ([8798f54](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/8798f547a60a7fa7c0849e20e1b0e9d012ac9312))
* **errors:** redirect errors back to ApprootDefault ([fbf21d7](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/fbf21d7313d7c2795c171b85a621ad2235eb68c9))
* exam auto-occurrence nudging ([a91fd7f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a91fd7fd6387e82331d881ec32e830fd59634d9d))
* **exam users:** course notes ([1e756be](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/1e756be7784101c7be80395efea988785a0b0d1d))
* **exam-correct:** accept grades besides exam part results ([be187ae](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/be187ae90727eba0966de59a3b6945080515b3db))
* **exam-correct:** add basic interface stub ([623becf](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/623becf59751f988411cb0fa39654d4fac214d0b))
* **exam-correct:** add basic interface stub ([cb7c9ac](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/cb7c9ac6dad5e248e9cf0748a871947369cd39af))
* **exam-correct:** add hasMore to no-op reponse ([e941083](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/e941083a44ae817f8ad301afdcf57ebdb9a1742d))
* **exam-correct:** add sortable style and date column ([87bda16](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/87bda1607e23fd44ac32279b9f36088554cdf56f))
* **exam-correct:** add sortable style and date column ([9fa4245](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/9fa4245607a1e31e915c67a3840dad291a9d284b))
* **exam-correct:** display backend error messages ([6fc0262](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/6fc0262d2dbf9e6a72b0db94c0a46cfb9ffeba15))
* **exam-correct:** display more info ([ef52f02](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ef52f02d78d8dfc75c48c1ad65b329d68c3361a5))
* **exam-correct:** examResult interface, no styling or functionality ([970076e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/970076e7307d18424837957133e0086aa78bbafb))
* **exam-correct:** explanation & length restriction ([1bf19a7](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/1bf19a76bd6c03ff23ab417fcfc0e86596dc896f))
* **exam-correct:** general improvement ([23044b2](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/23044b28dbcfdc82d5f1e934ab18fba034d60e4a))
* **exam-correct:** hide result grade select ([edacc20](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/edacc2016d6bbd5c25d62f13d8609b41ee5814ac))
* **exam-correct:** limit number of matching users (BE) ([d4d27f8](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d4d27f8ef63bf5374eab9e3a8e1d1bea6e2e5b3e))
* **exam-correct:** more on frontend name resolving ([905d445](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/905d445479f846edf724452832e6e9659c07fbbf))
* **exam-correct:** more on frontend name resolving ([daf9eee](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/daf9eee1d3203da5cd7893431a3358a12e41941a))
* **exam-correct:** more stub ([cbe6495](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/cbe6495609dd762f86d13638684496a1822b048a))
* **exam-correct:** more stub ([6727dff](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/6727dff2ef5de6cf3d4c302165c1c04302d41b68))
* **exam-correct:** overwrite request cells from response ([c8edbb3](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c8edbb395b1dbcd704864e640bce1354d1774c40))
* **exam-correct:** persist results and more ([a7cc24b](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a7cc24be90cb13985d0faf8c8464c921054f0345))
* **exam-correct:** persist results and more ([53ff629](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/53ff6298e2a5f12b2453dd6d9e2bedec6494ecda))
* **exam-correct:** postECorrectR stub ([5f9a176](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/5f9a176bc68116757237b499da26bb34e8aae32b))
* **exam-correct:** postECorrectR stub ([a525cab](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a525cab356427514c49348929a362a52fd6edc8b))
* **exam-correct:** request refactor and handling of sent uuids ([f06ca00](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/f06ca00d752ba0034055f8031e17d1f56594e007))
* **exam-correct:** request refactor and handling of sent uuids ([4a36a01](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/4a36a010f4bc0ea326f263c0ee77132843f03c73))
* **exam-correct:** resend option on ambiguous entries (TODO refactor) ([512f4d9](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/512f4d907095087688774bc6adf8619cda3b9142))
* **exam-correct:** resend option on ambiguous entries (TODO refactor) ([e252be2](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/e252be2fef3732f04b333a909860df985b786fd5))
* **exam-correct:** return user lookup result even for failure ([8e41820](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/8e41820c9dbe812530a6a5844c0f4baa0aa73027))
* **exam-correct:** server date handling in frontend and refactor ([77e39be](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/77e39be56c68af9bbf0fdce98508e4d10401dd06))
* **exam-correct:** server date handling in frontend and refactor ([d8a080d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d8a080d74d4d8ebe78c864daedcd0d4b0b6114d4))
* **exam-correct:** setup basic session storage manager, add util stub ([9cb64f2](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/9cb64f2a8f5c86d5b5ef5ad65fd7f3c9f0ef7a13))
* **exam-correct:** setup basic session storage manager, add util stub ([9a79156](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/9a791562b6809b54b6d45699566e0f369e14c627))
* **exam-correct:** single runDB in POST handler; more response handling ([4cb62f8](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/4cb62f8f91505c40364b51339072545173f1a569))
* **exam-correct:** single runDB in POST handler; more response handling ([6837c44](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/6837c44b7f40bd4544e75a91a70f6722dc3d5ea7))
* **exam-correct:** status icons (wip) ([3cc6814](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/3cc6814ff5e337d24841fc25797ca92e38c5bf3a))
* **exam-correct:** status icons (wip) ([eefff9f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/eefff9f719aa44a544f3eea25ba29b987d085365))
* **exam-correct:** stub ([90359c8](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/90359c83b75e52e369ad6a3df8022ed3cdb50e72))
* **exam-correct:** stub ([0467194](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/0467194e3df2da569bd2a4bd8c63bc2d23f44c30))
* **exam-correct:** submit on enter ([10de1a7](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/10de1a7de7e6855c6c84933618aa84d20f7f07b9))
* **exam-correct:** upsert exam part results (TODO) ([c0f91bc](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c0f91bccdd48dc0c99e3b8ef133cfedd890866cf))
* **exam-correct:** upsert exam part results (TODO) ([650598f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/650598fc22cf33d9eafb97a988db672d94cc3a48))
* **exam-correct:** use examId instead as uw-exam-correct value ([2d9a877](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/2d9a8771efb5bd8d3c22c799ad21bc1f750a4837))
* **exam-correct:** use examId instead as uw-exam-correct value ([5d7427a](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/5d7427ad463f114648f6d5b958e5ecba519f0abe))
* **exam-correct:** validate user input stub ([7f04862](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/7f04862a6f0726a7b721d8bbf1cc6c81b2ced5f8))
* **exam-correct:** validate user input stub ([431d004](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/431d0046658f6f096fb3bf4c11acba0337f10629))
* **exam-correct:** work on delete ([014036e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/014036e4e31f17267e0ad2c82faff163f6064d6c))
* **exam-office:** course/user opt-outs ([484fa1c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/484fa1cc632b16d21e694426ae6552dc9098a8f1))
* **exam-office:** exam-office permissions by courseSchool ([5841a7b](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/5841a7b5d2c9ee1b291a003f3d03b41d5e0b5d95))
* **exam-office:** exams list ([651f0bc](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/651f0bc4d47477f5f60ed1f91b038cfe6c74cf92))
* **exam-office:** grade export ([72a7f6e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/72a7f6e8a8edfacc6aa8ae477a8da46f6f88c551))
* **exam-office:** notifications ([52e1844](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/52e1844d5e5f5c38cdf639f58ba682af5cfc678a))
* **exam-office:** show exam(Occurrence) end-time ([b638783](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b638783f1262f8ea7817618f70474a114b182fda))
* **exam-office:** subscription management for users & fields ([f75cc64](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/f75cc641e22b6f00bc494f85aa64cd365ac19ad5))
* **exam-office:** user invitations ([123970a](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/123970a7833d4a10474bbdcc4dfa6102795d331d))
* **exam-users:** allow missing columns in csv import ([e242013](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/e2420130874728ad1e98ee5c590d0a20d5f2da5e))
* **exam-users:** document part-* family of columns ([fe07a22](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/fe07a226e9d5ee3195067e45d9c41d730218c7a2))
* **exam-users:** provide better table defaults ([a689d19](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a689d19bfa314ae7e559232c0c3d23948c211ebd))
* **exam:** audit exam registrations ([31931e7](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/31931e708e998116d112fb8a29a1434769ff9d1c))
* **exams:** accept/reset computed results ([72342f1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/72342f13936f9e4056e3825ba872a3c5a3726e11))
* **exams:** add extremely rudimentary registration table ([31e6b72](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/31e6b72c463e7f638a2cc2ff6f19b67f2d49db73))
* **exams:** add warning about multiple automatic distributions ([7fc9fef](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/7fc9fefb0a255dcf627320e89802fa5b6869c542))
* **exams:** allow assigning exam participants to occurrences ([e1996ac](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/e1996ac2e51d74db09c833b6c57a80fcdcb9f6bf))
* **exams:** allow forced deregistration ([1b532c4](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/1b532c4e4d2aa90da93a08dd4f1dbaf8626e8077))
* **exams:** allow mixed ExamGradingMode ([acffe04](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/acffe0435037b10cabdeffebd1cdcafa03b74d0c))
* **exams:** auth ExamResults by ExamExamOfficeSchools ([29a3e24](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/29a3e24bcf01cd9c893857eda00dcd249e6cbbe2))
* **exams:** automatic exam occurrence assignment ([e994faf](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/e994fafe28a32022c06c2cce123181525061f24e))
* **exams:** automatically compute examResults ([ea5a398](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ea5a398bab2ca0a63af06e167129c2656e887c74))
* **exam:** save registration timestamp ([78e4369](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/78e4369afb1f76c1a6a30580a2ae35273f495e43))
* **exams:** basic required optional action for authorship statements ([5cc41ae](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/5cc41aeef94993a24538b2f88af1fb75625036a8))
* **exams:** better display exam-result-information ([0ebda4d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/0ebda4d38243d54bf2638e4ce7808bbc084d10dd))
* **exams:** better explain "enlist directly" ([f07eb3d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/f07eb3dcc33fffb823fb490e36576a202328a0f4))
* **exams:** check exam_discouraged_modes ([f9c50c8](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/f9c50c80f22770f5376396923b8921eaac3e7216))
* **exams:** convenience for automatic grade calculation ([ec6a8ae](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ec6a8ae463ca42fd80538da782a864b739f6ba3e))
* **exams:** CRU (no D) for exams ([67a50c9](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/67a50c9e87d3368aafe7f52a3b81e580713e6c24))
* **exams:** csv-based grade upload ([932145c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/932145ccf794cffce396ddb2d85f01e74d1c7c75))
* **exams:** csv-export exercise data ([2218103](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/2218103cbd6a021fd24629f9215c71dd115f08e4))
* **exams:** csv-import of ExamPartResults ([29f4e28](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/29f4e2853667db251b48857bcb21d22482534f2f))
* **exams:** disable and set use-custom field according to school setting ([22dfd33](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/22dfd33aca9b8ad797c2617bbc656cf8276edf38))
* **exams:** display school default in form ([abd68ac](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/abd68ac0322a34afb62c416b60965e87ee6f10c2))
* **exams:** do form validation ([bf7b25c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/bf7b25ca9e9d11df94b91f7483ee339cefd3e0c9))
* **exams:** exam design & school exam rules ([f7bab3b](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/f7bab3befc4c42cde430699681f8caf8a959ab39))
* **exams:** exam finish button ([78d0f25](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/78d0f2522db759c2ee465e040939c92b2f9a1891))
* **exams:** exam registration ([99184ff](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/99184ff05322573a6958f09e30fa0fdcdd3d665b))
* **exams:** exam sheets ([500000b](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/500000ba0f6f3b3c32cfd7593e5468796660d46b))
* **exams:** exam staff & additional schools ([94436ee](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/94436ee0e1ce2cbf13a66f9ad81883d7286acb9b))
* **exams:** filter on occurrence ([cf040ce](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/cf040ce6863488f4708c1c2059f783413b1183d1))
* **exams:** first do-nothing stub for exam-wide authorship statements ([0392297](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/0392297ddbfccbb9a08e678696a9cedd1098121a))
* **exams:** Form validation ([6fb1399](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/6fb1399ef448eb1a6b92c652e644d6aaafe11673))
* **exams:** implement exam registration invitations ([dd90fd0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/dd90fd04a3ebf43e35837d41ca14424f7568bc2c))
* **exams:** implement rounding of exambonus ([e97cd56](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/e97cd5616bfadb838d3ba279768f38fb536dd4fc))
* **exams:** improve handling of exam results everywhere ([0e49bc1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/0e49bc14e5a66f75f75612a57024dace0303f299))
* **exams:** improve immediate exam table on home page ([93e718f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/93e718f32366b4f4b6cd083473f15b192aeb642f))
* **exams:** improve occurrence display ([2b56f26](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/2b56f26c45bb0c17bd2b4ad0a491b912c96e9acb))
* **exams:** introduce examOccurrenceName ([379a7ed](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/379a7edd12b16ed55d39e99637de647a51fb4267))
* **exams:** notifications wrt. registration ([ae27ff0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ae27ff0bb16e1daab034bdac7bfbf787f3a3a77d))
* **exams:** optionally close on finish ([4b525ea](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/4b525ea8246706d191fce109d4a9d1f5cc4c22d1)), closes [#652](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/652)
* **exams:** re-introduce ExamBonusManual ([54e94a6](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/54e94a667027548056a12139ac96512fc4609911))
* **exams:** refine exam form ([014a17a](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/014a17a3be8811586caea5d9f178c5cd318fae29))
* **exams:** show exam bonus in webinterface ([2b23600](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/2b23600a2287e96e5b482c4e53125a55b64bbb93))
* **exams:** show exam results ([b8b308d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b8b308d608a6acb9e70a143157437038de1f92ac))
* **exams:** show number of registrations to course admins ([ec020c5](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ec020c5486eb0573fa35a710b8ed0752d0443ea6))
* **exams:** show occurrenceRule in exam overview ([06673e0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/06673e00311d01dbc7b8844cf2ab2f049e045cf1))
* **exams:** show study features of registered users ([04bea76](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/04bea764f4dcafda1a07ca3a98f290433c207bc5))
* **exam:** start work on automatic exam-occurrence assignment ([282df86](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/282df86bc20b5ec884379c6c81f232abfb4631c3))
* **exams:** use template authorship statement settings if applicable ([57a259d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/57a259d8a2822ac1c593663e99f6e41163909c91))
* **exam:** working prototype of automatic occurrence assignment ([f89545f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/f89545f36ec4b0d45a0607b04d4b0d86b5dd1caa))
* external exam csv export ([553c117](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/553c1176269850048998ef1a0b8f580c0d4dc267))
* external exam csv import & ldap lookup during csv import ([1d14b6a](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/1d14b6a69cc569e3924523d4270a897c7529281a))
* external exams in exam office exams table ([3b739f7](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/3b739f751d195063665430fc144169e998c8fa55))
* **external-exams:** add actions to EEUsers ([2cf4895](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/2cf4895231e889711dc62896937e775b3d85fe79))
* **external-exams:** auditing ([2b153c1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/2b153c1863752ddaf7a8f476fc9696448fed17e6))
* **external-exams:** create new exams ([94bb391](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/94bb3911cb7dc1d12544be5a644ff0b7ec25725a))
* **external-exams:** display staff & add' schools ([c14d90f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c14d90fd531a9d391f90df3c27724c0d3219f2ee))
* **external-exams:** edit existing exams ([1252a5f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/1252a5fc79354df10bd0b8d17953fb306ae5024a))
* **external-exams:** list ([fa3521d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/fa3521d6dbebe1d07352bec2269b10e5eb3e31d5))
* **external-exams:** open defaults wrt. external exam schools ([ef1411e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ef1411efdb644e300656403c071cfcdef9caf077)), closes [#651](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/651)
* **external-exams:** plan for student grade access ([b7506a0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b7506a03b1de498c3231e81c3330994d55dbb54e))
* **external-exams:** requisite routes ([f25b21a](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/f25b21aa4b9f4be71bf1afc7cdfae58f7f93c689))
* **failover:** treat alternatives cyclically ([9213b75](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/9213b7554a6da2a40ea0c82ad4601a951dd7ebb4))
* **faq:** exam-points ([aebc05d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/aebc05d021dc27c6539373b7e30a1f97898bf15c)), closes [#595](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/595)
* **faqs:** i18n ([a1a0fa3](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a1a0fa3a448c75f83949eb9f3a5f681fcd6e5792))
* **faqs:** initial ([7b53377](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/7b5337723d6dd300dc18a0881c589f89dea0bdbf))
* **faqs:** more faqs ([18766ed](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/18766edc7c9757d26a9d83f1b996fdc473f48f8a))
* **faqs:** more links to faq ([10d44d1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/10d44d189bb7df852bdbc64466e31f09fe91f619))
* **favourites:** usability improvements ([fccc2ea](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/fccc2ea212fd5d780510ee6b59191cd1d615c8b4))
* **fe-heatmap:** add css class heated for heatmap elements ([b09b876](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b09b876969f43cc0d21af9f9c0057c1285811e3c)), closes [#405](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/405)
* **features-of-study:** record parent & standalone candidates ([2621d36](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/2621d36b7d020e67b66e0371004634decc5208fd))
* **file-uploads:** maximum file sizes ([9dee134](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/9dee134b11adb7f72ecb66117c4373c08a664979))
* **files:** avoid initial unnecessary rechunking ([e80f7d7](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/e80f7d7a89e205ce53a70178e0b44d9b0ddf5b97))
* **files:** buffer uploads to minio ([d9e9179](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d9e9179a52d1c17633b6dedae7d2a263f3612ac2))
* **files:** chunk prune-unreferenced-files finer ([58c2420](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/58c242045887673f69c368668803574d829cc823))
* **files:** chunking ([8f608c1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/8f608c19552ef7bd6ce61af92496b3d5f5bf61b1))
* **files:** content dependent chunking ([d624a95](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d624a951c54bda86e04d440eba9901d2a65153b9))
* **files:** further balance file jobs ([1926917](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/1926917dd7463a4ed11b9e7ee64fab6c8167de6f))
* **files:** monitor missing files ([fb0ae65](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/fb0ae65ac5928443abc01de9b57c69849d6a6b21))
* **files:** move uploads from buffer to database ([9a2cba5](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/9a2cba5c0acd0db489d5958938efcdbf6d2dcc63))
* **files:** safer file deletion ([88a9239](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/88a92390d580d618b15081c87dabe51c7c5e0eca))
* **footer:** add link to source code ([5a88d5c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/5a88d5c41f39a45147b049985d2f59cf70daddec))
* **forms:** allow customisation of user-facing datalist values ([412ce98](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/412ce98fa0efc2715a9ba75ba8e95786fef47450))
* **forms:** improve field labeling & error reporting ([3820b45](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/3820b45b3e87de3d7ba43a11d84666227f42582d)), closes [#588](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/588)
* **forms:** Introduce more convenient form validation ([f8d0b02](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/f8d0b021edcf254c161ff98e1183e9e4bfab0df9))
* **forms:** show studyFeaturesField in studyFeaturesFieldFor ([b7496f9](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b7496f994075836949a9f6f5c584fa34a2441d1d)), closes [#451](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/451)
* **foundation:** move stuff out of Foundation ([e27beba](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/e27bebac59e42857b82e567f07576167bafcf8e3))
* **frontend:** password visibilty toggle ([f0e4547](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/f0e45477fa85a1d82750597cdaf122e41e9c7764))
* **frontend:** split up util registry ([67e472f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/67e472fa5e09ed2068d477ef12b12adb0ca98c4f))
* **frontend:** use webpack more extensively ([5d8c2af](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/5d8c2af51d69c5d33c84447b7baabc75e34d930a))
* generate & include new favicon ([b78c484](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b78c48465a4f42366c9a8e8ba924b8d5c2315d71))
* generated columns tooltip ([2c4080d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/2c4080d0e0d7f59829238830a5200116a9d884ec))
* **generic-file-field:** prevent multiple session files of same name ([98e1141](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/98e1141e602b08d422ae0db1d25b24b35e6e3238))
* **glossary:** english glossary ([237c586](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/237c5868b708e6f5734eef19c63f1764eb3cbdfb))
* **glossary:** more de-de-formal ([7daa42d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/7daa42db983c815dbfc65b50c59a10dbc5054bda))
* **glossary:** most glossary entries in de-de-formal ([ba7c60e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ba7c60ec1ce697e65a9a507073f03c540bb20d41))
* **guess-user:** add option to limit query ([4154a39](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/4154a395f44edb059225caa090bd3ba95a1451c2))
* **guess-user:** replace guessUser and usages ([ca96518](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ca96518e0eb348b91964d21680b1e8e8d3600fa3))
* **guess-user:** variant of guessUser ([58ae9dd](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/58ae9dddbc994f17cc52cc0940d996dedf583ba5))
* **health:** check for active job workers ([d1abe53](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d1abe530b60939f69289b60216f52eab7e7ba6a4))
* **health:** timeout all health checks ([33338cd](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/33338cdfe94754759e4aa8cbf5ccd9f9fc939fa6))
* **help:** attach last error message ([fdd6b1a](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/fdd6b1a194fd2d4a1deb399cd3914e63e167d30a))
* **hide-columns:** add hider label th attr ([6c05a8f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/6c05a8f09fdd0d1679c3c5b33277829901d2e8c0))
* **hide-columns:** add hider label th attr ([71e90a1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/71e90a18178dcab90ee68519b15e44e51ff79a91))
* **hide-columns:** add hider labels for material list ([ccafd95](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ccafd955b9b9659d05e34aadd0bd8fdc15ac44d9))
* **hide-columns:** add hider labels for tutorial list on course page ([3553df2](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/3553df23ca75eab3c324d9b67aa11ce01212c230))
* **hide-columns:** add hider labels for tutorial list on course page ([03e4ac1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/03e4ac1ccac94dc815b9341fbd547ee548d5f839))
* **hide-columns:** add more hider labels ([555c4ae](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/555c4aebebbd050ec00ad0b4369c196502301d7a))
* **hide-columns:** add more hider labels ([eba58d8](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/eba58d83a04009f712f46fb2a4b123081b8ead84))
* **hide-columns:** better positioning of hiders ([761c6d3](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/761c6d39a82a57e4bdddd193d25be459b12ca1f7))
* **hide-columns:** correct storage keys ([610d13a](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/610d13a7292f61e7892bc0f58a666011efbbbe71))
* **hide-columns:** don't break on dom changes ([c519792](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c5197928b12861599338a87dda9562ff16782332))
* **hide-columns:** fadein transformation ([506f94e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/506f94e5d4afd2e2a955dab654f9a566a4152728))
* **hide-columns:** first stub of hide-column util with manual styling ([111821d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/111821dcad4d0ffde6dfebbb62fb21b9b76ab9c5))
* **hide-columns:** get table wrapper ident for storage ident ([d55d3ef](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d55d3ef4847f089970982e9554365f8699f2f9e6))
* **hide-columns:** hide empty columns per default ([d1232ce](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d1232ce72d45ddcabe14f9770a0f8117d8efe74d))
* **hide-columns:** more (broken) styling; move hider elements in DOM ([e655bc6](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/e655bc6e700f1b9668633f6beaef7acf8db484f0))
* **hide-columns:** more styling ([4908702](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/49087027b250a8ce1212d508e1c9028f5122a09e))
* **hide-columns:** opt-out on select columns ([b03c10f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b03c10f09810796f43d313dd17b15f5c39c8e5a3))
* **hide-columns:** refactor and auto-hide empty columns ([047c0a5](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/047c0a5787657be7f810b20599bb1015a99b4e9b))
* **hide-columns:** set attributes for hide-columns and extra-stuff div ([169a479](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/169a4799b4996c9474ee492e79dcd0a5040af04e))
* **hide-columns:** styling stub with repositioning ([a9c17d7](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a9c17d75fe2ec2aef65e4150d8f080797433fe0d))
* **hide-columns:** support colspan & don't persist autohide ([0798d68](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/0798d6870e7b4dfd0baf23940fe5d87b9c2d444c))
* **home:** allow users to define exam warning time ([d23e222](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d23e222fd0b2eb45afd28a5cb96cd25c433cf0c2)), closes [#445](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/445)
* **home:** clean up homepage ([a6e2f64](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a6e2f6491048186415546f5cbbd513b75c123026))
* **homepage:** add convenience links to term and school ([83445c4](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/83445c4e7707febc569f4bff271f995ece64dcd0))
* **homepage:** add prime action new course to homepage ([2208368](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/22083685961ca0503cce168c97bba73beaec2ea7))
* **home:** show immediate exams on home page ([242cff3](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/242cff30600a857efa9b98a44b44cbb73a1b1001))
* **http-client:** baseUrl and defaultUrl ([693189f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/693189fe8274fea64596d09f158a1121196c35eb))
* i18n form ([2d95f35](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/2d95f353c1209a4d3528c6aaf53c832bf5429a34))
* **i18n:** 12h-clock for english locales ([331ba1f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/331ba1fed3ca5e25942c906b10f670e2ed03299b))
* **i18n:** additional en-eu ([83a458d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/83a458ddf530941663108db705067fe129fb8bcc))
* **i18n:** basic language switching ([352bdba](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/352bdba1a4377bbd3ecf76b72c6aee6fdd861316))
* **i18n:** close language select on click anywhere ([97a29ec](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/97a29ec68cb64e4264afc1d6a95e73e608b33748))
* **i18n:** english imprint ([7b3ed79](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/7b3ed79ddd4c1d7d29c4830f7f5929662e9011aa))
* **i18n:** english versions of imprint and data-protection ([4ee3ad0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/4ee3ad01bab9b8d57ae266904a259130a5c039d4))
* **i18n:** get started on en-eu ([75677dc](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/75677dc171943cbdd4abb872dd813a9bea40e40e))
* **i18n:** missing message translations; small fixes ([aec4b21](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/aec4b21757c4d38622db30d96faa95b4fcaa72c3))
* **i18n:** missing translations ([153bb1f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/153bb1f62179e19e34f5c818544b86633a826e0a))
* **i18n:** more en-eu ([67e40fd](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/67e40fd3e71a90b13ce75be33cbad7c229b1899c))
* **i18n:** more en-eu ([3058737](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/3058737021a5f9f949d2da7a3ca608eb71f34cc8))
* **i18n:** more en-eu ([7c8dbc9](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/7c8dbc9dcc0c79bb50f464eb565d1cebba83b4b5))
* **i18n:** populate frontend datetime locale from backend settings ([498d616](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/498d6168a0f47b0f93bab75d65b87c34670535f1))
* **i18n:** store language in user account ([f0f9411](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/f0f94112f4d8b9af96c2048a046e43ce7ae7351b))
* implement in-memory cache for file download ([36debd8](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/36debd865f6e74856c74bd658dc4694140183fed))
* implement system-exam-office ([42aee66](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/42aee66d1f9c189a6a6b13b1970c61e0299630ae))
* **implementation:** add paragraph about license (AGPL-3.0-or-later) ([d4b341b](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d4b341ba259e710a142d813ede83c209fe2fe45a))
* improve logging/metrics wrt. batch jobs ([d21faf4](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d21faf4de0d40a3683ff2a7a3020bc85717f827c))
* improve navigation ([95ffda2](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/95ffda25b6f1a6e9e8ae0b0be5e58a7260c4f5fa))
* **info-lecturer:** add english translation; minor fixes in german ([a4fc555](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a4fc5551f1936a1e8fb5e7c28a2911fbc1ae8803))
* **info-lecturer:** add expiry time for newFeat ([fa9e6b5](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/fa9e6b587b2143160ad2067923b55592c7db64ea))
* **info-lecturer:** add inline newU2W icons ([5a49feb](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/5a49febf9c484bb0cf40007fee3197f206b5488e))
* **info-lecturer:** add newU2W icons on info page ([9f02ef0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/9f02ef02b3cfa2b4f7ee5ace2cda1a492f76ee5a))
* **info-lecturer:** minor adjustments ([64b391a](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/64b391a0feb8150c5a213a5ccf7e3eadddeaa714))
* **info-lecturer:** more bullhorns ([4a5e7d9](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/4a5e7d9e7e1c5cc8f56169401693fea3f4c65a8a))
* **info-lecturer:** remove "news" section ([cb1e3a6](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/cb1e3a604b305ca45584bc009f4bf0c027613e22))
* **info:** info seiten überarbeitet ([7459fc3](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/7459fc34bc747fe7af31a4bc87930e1bea03c923))
* **info:** start glossary ([73b0546](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/73b0546db674914ca24e0f69c0c17a309e88fc1e))
* **inject-files:** additionally throttle by file count ([3cf0335](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/3cf033560e90ddc104e4056c470459f92b6eb4ae))
* **invitations:** additional explanation for new users ([bb9c34f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/bb9c34fa4de6efc811e6a336a8e68f924ff37b56))
* **invitations:** anonymous invitations ([1380d9d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/1380d9d21ea457ad631998c64b63d6aa85b764ce))
* **invitations:** save expiresAt to DB ([1c2f2b7](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/1c2f2b7221d2bb237f3f1da61ca1cd9cde791506))
* **jobs:** batch job offloading ([09fb26f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/09fb26f1a892feba32185166223f8f95611ea9ef))
* **jobs:** move held-up jobs to different workers ([284aae1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/284aae12135ad97b1cf85b45f1176da6930876ee))
* **jobs:** queue by jobctl priority ([a27a553](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a27a553e0a9782eda6023ec0b8b1055757bb511f))
* **ldap:** automatically synchronise user data from ldap ([b39ba8b](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b39ba8b268ca705e93398175df55f8cb741c376a))
* **ldap:** expose active directory errors ([51ed7e0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/51ed7e0a26a94d2178a4ca10ad7ea36b99076b54))
* **ldap:** failover ([0e68b6c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/0e68b6cf5348bbf5baa5014a86be321a7e5e4b49))
* **ldap:** manually trigger ldap sync ([83afb6f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/83afb6f15fb107b5302958020dcba4018f98ba8d))
* **lecturer type:** aenderung ([89e1d67](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/89e1d675c3be0fec106e84920184a8c95dfa6346))
* **lecturer-info:** add planned features icon; update info ([a4068b4](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a4068b4a82931f1bc40d0edceca53297e0e45120))
* **lecturer-info:** fix typos, add info (adding tutorial participants) ([5139825](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/5139825aad1a9ab602000e2d870e424da6b84e48))
* **lecturer-info:** replaced icons with icon-tooltips; edited text ([2ca7085](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/2ca7085ec95c981fd4721a952064b3df8dbccb58))
* **legal:** fix translations and links ([cdc4053](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/cdc405307613eae1ef30b19e417faf27d6aee4a5))
* **legal:** move legal info to one single page ([565c6a4](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/565c6a4f3d3faabaee7d52f6bdb701c703aae7a6))
* **letter:** allow printing of multiple course certificates at once ([768f03f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/768f03f6727f54b7c7aa18ecef8bc67302ee27cd))
* **link password time:** application restore ([6d536c3](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/6d536c39bd9f3117f18d2e52c93f178aea4a002d))
* **link password time:** done ([4490e9a](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/4490e9ad20c55153e81a344c7dbf7813cb219108))
* **link password time:** done ([2321216](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/2321216b0f4f194c7cd8b47eb020819d6aa1f2e5))
* **link password time:** new time format ([df2a9bc](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/df2a9bc20fe9f958cbee98315b644ec2fcba0630))
* **link password time:** restore application ([c5c5417](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c5c541709b5053c08d21bdd753bb99df574c6c5b))
* **link password time:** restore application ([85006ff](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/85006ff389188b56a8b61943621c190c9a9503b7))
* **lms:** configurable csv settings for lms direct import and export routes ([6159403](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/6159403b27dab30178645dc37c99d41b4aaf610c))
* **lms:** enable upload handlers for all upload routes ([a5121f0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a5121f0d3e7a77695a6198057afd23f5f86ff174))
* **lms:** random ident pw generation without db ([21b74a5](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/21b74a5d7ff3c03be466ef911fbb8ed2a1b67f4b))
* load shedding ([9df0686](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/9df0686086ff7b64d401a2302edd2fe7636db111))
* **load:** allow creation of submissions without login (w/ token) ([2e826d3](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/2e826d3c4585b0dafe8ec7abbdd63e23f1d5d341))
* log ldap error messages on invalid-credentials ([0b4fade](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/0b4fadedd2d7ffbb58598d9844e1c7d97cabc447))
* log sent notifications for analysis ([c5ef6bb](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c5ef6bb5a5430a5f9b4dd4b2a5635332d1edfed7)), closes [#535](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/535)
* **logging:** additional logging for inject-files ([cbf41b2](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/cbf41b2ea061aa276f455dde1e31464d106cd3d7))
* **log:** remove container log setting in order to use stdout ([8f460bd](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/8f460bd0a3aba9013f2ee2d20a1465487ecfe629))
* **lpr:** print center allows filtering by day now ([cac4870](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/cac4870c95f5367536ee48644fea8a526a0da5a3))
* **mail:** archive all sent mail & better verp ([1666081](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/1666081fea0eec0bf5440a100db0e8cc69be8295))
* make git revision accessable to nix build ([b37c2e6](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b37c2e6aec125952c26156ad599f18496d5cea8e))
* make pagesize changes load async ([6486120](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/64861205361bb6ee25b172528bbf939850ba3efd))
* markdown help requests ([06f3ac6](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/06f3ac656313dd751f8a758349f6151497dc4ddf))
* **mass-input:** automatic add before submit ([7540a4f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/7540a4fe5fe0f61d449bc7cc5f5aa7d3da034f55))
* **massinput:** reduce size of ajax requests ([72838e2](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/72838e2592f159ab79c9f245ac28a4f9bd807e19))
* **memcached:** introduce general purpose memcached ([e8c2dc5](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/e8c2dc5aaa5baeeffef805752a7639c9e412dc21))
* **messages:** implement custom parser for message files ([bb877eb](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/bb877eb81396211a801496061ea603b39753829b))
* **messages:** mkMessageAddition ([ea33d84](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ea33d844cc4acb2503fc4780c7895299eb9d5ef5))
* **messages:** rename subs grade ([534c32d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/534c32d9f90fc95a5cf7ff16056ffffeca8cf964))
* **metrics:** basic collection & export of metrics ([b8f41ef](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b8f41ef0b36c092f734f9ee901b2f438ea21aab8))
* **metrics:** measure file i/o ([4801d22](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/4801d22cb360dcd936c57494ff2ff02655431409))
* **metrics:** monitor job durations ([0da6c49](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/0da6c493924ef544208a3a330bde7714a61fd835))
* **metrics:** monitor job executor state ([b74bb53](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b74bb53041073546df1b45b03758b559c98b95c8))
* **metrics:** observe login attempts ([0c7e56f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/0c7e56f4054593eef757dffba49fdc27f7b060df))
* **metrics:** report on health checks ([bec4023](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/bec40236dbdedd022b968e83f563df75ab35c959))
* migrate indexes ([dfe68d5](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/dfe68d5924d37ea4d3fd0df0a8e68871bcd187d5))
* **migration:** switch from versions to enum ([f2fb7d8](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/f2fb7d8c267fed96f0dbdb237f4984c8996fbce8))
* **minio:** use separate bucket for temporary files ([1cd79d3](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/1cd79d35e2761d84bb904a77e74d5cacb0b2244c))
* **monitoring:** observe database connection opening/closing ([d801a2f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d801a2f84ae42862dfc357a58ee47dd6dc39eef8))
* more date & time formats ([936c366](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/936c3666fcc39e00ef1c868b3f23d4bb88336702))
* more en-eu translations; minor fixes in de-de-formal ([870f1df](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/870f1df4d30182ef08d4c7b2f8bb15a6ea722925))
* **multi-user-field:** improve placeholder ([2936eef](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/2936eefbd1626fb69afa735df2d2dae5783ba842))
* **multi-user-field:** multi-user-invitation-field ([c072b85](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c072b85299f87f6ddfcdf72ff3db4881a079af41))
* navbar header containers ([1348c91](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/1348c91c3c7a5c645decda9215c352fa2130aaaa))
* **navigate-away-prompt:** prompt on actual value change only ([293ab6d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/293ab6dc62725a2b9785b18d7f06ff8efbf860ea))
* **news:** active allocations for lecturers ([cde0122](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/cde012252907c57506eef44868b3d25fae50a8f7))
* **news:** show system messages ([0d39924](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/0d399247773a0e4799602c49e6c06de906b43fec))
* **news:** timeout sheets after a month ([31aa25a](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/31aa25a1fd6acd0994e2af156b4b166b3717de13))
* notification about externalExamResults to exam-office ([a304840](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a30484003ae75ccbb98a4d275423320bd9d09f33))
* **notification triggers:** redesign interface ([84c12b5](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/84c12b5fc7c875940900c2c38cf59b09c0d63fab)), closes [#410](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/410)
* **notifications:** add NotificationExamResult ([a7e2921](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a7e2921a731d74eb4b79d60e8e1f34aa90161b60))
* **notifications:** sheet-hint & sheet-solution ([f11b215](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/f11b215773fb2af8cb400d5798029802768a631a))
* optional ribbon ([c2e13cf](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c2e13cf4df1fc8a6e0d919b5171a7eaf002fd381))
* pageactions for exam correct interface ([0d4dcf8](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/0d4dcf8658d7b6d6f3fe692400c772425bbfb3b0))
* **pageactions:** finish restoration ([e1cac76](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/e1cac76f1518e2de0f9d7e9f0f80278ea07fac0d))
* **pageactions:** restore pageactions ([4bc48a5](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/4bc48a50faf4fbecff14fd7e38a31503097d74d8))
* **pageactions:** restore pageactions ([926bd44](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/926bd4473696a7d1524659895975749d7b4b3a79))
* pandoc-markdown based htmlField ([c5848b2](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c5848b24e850eb0bfc13db3ff68fd05df522b057))
* partial support for lsf import ([37cdc77](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/37cdc775b5b2d3e4cd1cc22858b2c05e75de8a3c)), closes [#686](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/686)
* partial/conditional downloads & video streaming ([5b28303](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/5b28303539e28024b43addb413aedc4e5ee0e470))
* participants intersection ([697c3e1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/697c3e11fc3bdd279c0df2d4fd9362bf2981ccae))
* **participants:** basic funktions added ([b96327b](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b96327b18dafcd020c94bb84c6aafffb53544076))
* **participants:** corrections ([fd11121](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/fd111215447aff817399db379a4ca8e90eb73cff))
* **participants:** corrections 2 ([d6ce0c4](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d6ce0c47d92fac76ccdc59805fcdbd3ad932d3e3))
* **participants:** first finished verson ([0a3fd23](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/0a3fd23e22a81b3636fb3ac224dce52df3f752f2))
* **participants:** second version, Intersection added ([02354f0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/02354f0998e61c236bc982848b9d709c927690f5))
* **participants:** small Name-change ([6f3243d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/6f3243d90bdc137e7f2ea9fe8e271f1cdc32dfbd))
* **participants:** small Name-change ([eced778](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/eced7781ae346e285b7f3949917f23883b4dfaa8))
* persist bearer tokens in session ([d8040e7](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d8040e7aa864e2078962ed2c938ae91408dd9e50))
* **personalised-sheet-files:** collated ignore ([1fe63a2](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/1fe63a23a0b41dba3b87b97903eb58ced87f8b2d))
* **personalised-sheet-files:** download from CUsersR ([93d0ace](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/93d0ace8ba97d19edba92a65b3f37a793165baab))
* **personalised-sheet-files:** finish upload functionality ([ed5fb6e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ed5fb6e218097250f197b7795d448bbfe460bf99))
* **personalised-sheet-files:** i18n ([f452b2b](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/f452b2b24f60e3d2cfefee0501d9833e2f7665db))
* **personalised-sheet-files:** introduce routes & work on crypto ([9ee44aa](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/9ee44aa2f1aaa91c898b216f0dba58017122c75f))
* **personalised-sheet-files:** participant interaction ([db205f6](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/db205f635d8c80b037f96f34488fde451140eda7))
* **personalised-sheet-files:** restrict download by exam ([a8f2688](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a8f268852a256209f6ab187167c2a1c066618c4c))
* **personalised-sheet-files:** seeds ([cf67945](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/cf679452928c14200e1eb3877987ee299fbf9f6f))
* pruning of unreferenced files ([ff161b2](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ff161b2e045dcd1281aec21513ed1c818f2174aa))
* **qualfications:** renewal actions and filtering by card and personal number ([4df0243](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/4df024374d387fc85a833b3faffe1b6ef8edc7d9))
* **rating:** pretty-print to new yaml based format ([2bf4846](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/2bf484609e4efb905614ffddb3b8f0dee03f5483))
* **ratings:** i18n rating file names ([1195231](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/1195231bc3d3502fa4f77db64d30f2138cd7fa20))
* **ratings:** parsing for new format ([af79473](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/af7947328d38ef6db4450bae177efdae7877ab56))
* reduce number of study features for courses ([51a98f0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/51a98f067086bcef3daff601b53d5eb45f4a27f0))
* refine presentation of exam-correct ([95c1755](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/95c17557107c3ff41ea85cd32d3e5ff435feebb9))
* rename "Start" to "Beginn" in error messages ([66bd10e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/66bd10e4142cee5f1683e1252885f1b5a0a07fa6))
* renamed "Bewertung abgeschlossen ab" to "Ergebnisse sichtbar ab" ([6b610e1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/6b610e1e54cbca89bd957101723e9c79652d315a))
* restore & improve navbar contents ([51fc6dc](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/51fc6dc541ea729c049271d3f1d16afffd5ef6c0))
* restore study features in all tables ([363f7ab](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/363f7abc192872ebd2a609b8bd89b58032bc9131))
* **robots.txt:** disallow ahrefs ([9afee89](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/9afee89a090030c31b6025eef22b5055a4251c0b))
* **rooms:** different room types & hidden rooms ([319c75a](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/319c75a85aa5e5f7e2f2af328d69960e1df3cb80))
* **schools:** add school settings regarding authorship statements ([cb8e338](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/cb8e3385889c0c4c13418bc69af091b9c8a3f22f))
* **schools:** implement cru ([18ae28a](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/18ae28abbcfcff6015b419f6791bc60fe6dd88f5))
* **schools:** more school-wide configuration authorship statements ([960bd76](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/960bd76acafc9cd077b831b67a281eb7b20e703c))
* **schools:** store school authorship statements as html ([09927ae](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/09927ae14004f7a27f816ad874704969641dad83))
* **serversessions:** move session storage to dedicated memcached ([9960059](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/996005935df86cbbc48bd823cc8cba13aa2f8bca)), closes [#390](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/390)
* **sheetlist:** sort sheet file types in db by haskell Ord ([643cc41](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/643cc4165fdec197ae8f744f193a731e731f537b))
* **sheets:** add required flag and definition ([541dd76](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/541dd7688ffa36be8a968f26f920507ed5aae646))
* **sheets:** better explain rating-done ([3944ce0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/3944ce02615f8f4d3ec6a14ae4beaceb324c6104))
* **sheets:** display authship req on SShowR ([44473b4](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/44473b45756c5df20e6a81927867de191cf70366))
* **sheets:** eliminate authship statement required Bool ([0735c05](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/0735c05a7489957ed500bac1c006f4ecfdab74f3))
* **sheets:** fetch school statement as statement default ([a39a0d7](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a39a0d7c8763e158dae5750afac8a78bd953dcdf))
* **sheets:** introduce sheet-specific statements for exam-unrelated sheets and as exam-statement overrides ([3f87f20](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/3f87f20eb14e5db8a63c61885c4570689169ebed))
* **sheets:** pass-always ([b2ebce4](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b2ebce483658d74239b7a9dd5462b7c78371b896))
* **sheets:** require exam registration ([d770afd](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d770afd2c6ade597fa2b8ecf229c312f1ee6be56))
* **sheets:** submission groups & rework sheet form ([57f1ce9](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/57f1ce9265e2a122aa7a318b923df51ee9ead0a3))
* **sheets:** upload-empty-ok ([ab1940c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ab1940cb09e824fbba03264b5451fa8b17c5c804))
* **sheet:** warn about no submission without not graded ([9373266](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/937326639a02c576f278b79b8ebb441a2652bece)), closes [#342](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/342)
* show authorship statement requirement for sheet ([5e96982](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/5e969825ad0c84c240b5c17b011dacbb63f4bfdf))
* **sort-table:** add basic SortTable util stub ([53131e2](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/53131e2de891edc57d900a618aeadc31533f305b))
* **sort-table:** add basic SortTable util stub ([11c0bd0](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/11c0bd07e93a4a59d78226dbf3a2ca94d02a5883))
* **sorting tutorial table:** application restore ([9dc12de](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/9dc12de056e73736659c053b0eabef66ca524047))
* **sorting tutorial table:** done ([482241d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/482241d033c32c52c31ea20920a4fec07ba975dd))
* **standard-version:** allow adding additional changes to release ([7ed6fe4](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/7ed6fe4fedc2f0ee463680a0d3210d7a6f4ac7ab))
* **standard-version:** complete release workflow ([605e62f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/605e62f4458238119eaf13b3e8151924d758e3a9))
* **static pages:** touch ups ([d2c0043](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d2c0043debb0896446cefaef5f488e99470bbf39))
* **status:** show instance running time ([8743719](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/8743719183abfa10d089c6e7e765e49f6da3c50d))
* **storage-key:** add breadcrumb and import ([8cf5d63](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/8cf5d63cf2d63d3fa0017d3c555a2070431086f6))
* **storage-key:** add breadcrumb and import ([1580d3f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/1580d3f59bf4d716228ecc15c8af17b8af445250))
* **storage-key:** add StorageKeyR to routes; minor Handler refactor ([2d1d58f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/2d1d58f78f3bcdce5030da2eb2e67532f3ccff27))
* **storage-key:** add StorageKeyR to routes; minor Handler refactor ([4d4dc8f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/4d4dc8f58be9f0c705b52d6bbd42041d41b2749f))
* **storage-key:** postStorageKeyR ([059efe5](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/059efe5085967366df663ca531f1e4fb521ac9ab))
* **storage-key:** postStorageKeyR ([b51c466](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b51c466a650743d9df9e954313e9c7501f66c4f0))
* **storage-manager:** add en-/decryption stub (WIP) and restructure ([54d852f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/54d852f30823c59146d75d33f05f35f2c66a228f))
* **storage-manager:** add en-/decryption stub (WIP) and restructure ([0016145](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/001614522e0ae3841b27086e9370c999c24a2963))
* **storage-manager:** add storage manager library ([1023240](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/10232401369fa18fd7866a584b4f6d3eb1380e5c))
* **storage-manager:** location hierarchy ([80ff4ac](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/80ff4ac2a734bf295a026aa68d5d881da9cca3bd))
* **storage-manager:** store encryption info per location ([25a7c34](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/25a7c3420a68ef6a959506db27411dc363c1b839))
* **storage-manager:** store encryption info per location ([8122ab1](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/8122ab10b0277069ec4a6de7f77dd34748df3fe5))
* study feature filtering ([96d0ba8](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/96d0ba8f7a1c8d8d4e895541b66e36d35392fb25))
* **study-features:** add study-features-first-observed ([dcb83d9](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/dcb83d96fc0e52c0c322e50d9467d9a2bed90359))
* **study-features:** cache study features term relevance ([8f6d54d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/8f6d54d0125e01f5c8a90843b54129d6412b79f1))
* **study-features:** complete StudyFeatures admin-interface ([c4c82f5](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c4c82f54396abd6cdb3c719079a6f87a883c4989))
* **study-features:** further restriction by course ([f7a9bc8](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/f7a9bc831a3b0ef58fcbf7918be9f5e3b262641e))
* **submission-groups:** invite w/ submission-group & audit ([7f10d44](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/7f10d44aee0fea561e331e20d03ff814a6df9baa))
* **submission-list:** bulk download submission originals ([d7f2d11](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d7f2d113929f9dc11291d6db916c8944ae158c3b)), closes [#707](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/707)
* **submission-show:** display authorship statements ([cbd6d7d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/cbd6d7d2b098f8e2c921fd7a56a458d62331d784))
* **submission:** add correction to sub-show-r ([e060080](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/e06008026160185103b9adf60b5cfbc6991d77df))
* **submission:** allow restriction of submittors via token ([0fa8d37](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/0fa8d370374ae8022faa0ebd7dfcc5a50afa4e4c))
* **submission:** edit notifications ([98c0d69](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/98c0d6919e0e4ef4fca6eb451edbd8283829ebf6))
* **submissions:** also warn correctors about multiple submissions ([8795edd](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/8795edd1fa452d012704146481c8318d206634a5))
* **submissions:** display authorship statements ([7749238](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/7749238e554b612a8bf69e6beb94efe3e5d02973))
* **submissions:** display submittors more explicitly ([d2e2456](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d2e2456f6204245d933fb6abc87c44388ce3e339))
* **submissions:** ignore additional filename components ([38f69c3](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/38f69c3aedaf497753a5240d1d64970320f4f64e))
* **submissions:** improve behaviour of sheet-type-exam-part ([91a5166](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/91a51664c32bd17e4c2d1cd496bf05338146291d)), closes [#676](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/676)
* **submissions:** non-anonymized correction ([fd2c288](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/fd2c2881ea5a465458eb3f64d7767be3a307dc46)), closes [#524](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/524) [#292](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/292)
* **submissions:** optionally disable consideration for deficit ([c6a6ec7](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c6a6ec721c2a863d324ddfb5d2b2c1e42e659067))
* **submissions:** warn about multiple submissions for same user ([c19a00d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c19a00dcefb2dcae017026edb6e1c7cb6ce16841))
* **submission:** warn about deleting co-submissions ([e87f607](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/e87f6075d379eac84decaf0079cf62d8d5d0698d))
* support exam registration including room (ExamRoomFifo) ([14bb020](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/14bb020fe9d8f7579269468c9fb55a1dc373d145))
* support for ldap primary keys ([bbfd182](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/bbfd182ed93d1e602229a2fd1ac1e0fa4c4439ef))
* **system-messages:** hiding ([c81bc23](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c81bc2314e75d82ad2a246218b7e077d5cb02781))
* **system-messages:** manual priority ([cf06f79](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/cf06f798072a5c4cd1a7f6f92c035929f467ff30))
* **system-messages:** refactor cookies & improve system messages ([ead6015](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ead6015dfef7e667e52116b26fd92ebfd4f908eb))
* targets on InfoLecturerR ([5ffee38](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/5ffee38979e239c40390c4c5e4ac8db1532bbede))
* **terms:** better prediction of term dates ([e5732df](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/e5732df1b62756aa267fbaad598f96478cba0220))
* **terms:** improve term display/editing ([8b7e8e4](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/8b7e8e4bd541dec10e098485453b6a08c758bb68)), closes [#485](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/485)
* **terms:** time based term activity ([df073ef](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/df073ef7947eb80dc35fe955b92e635881eb50fa))
* **theses:** additional state explanation ([1e38734](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/1e3873485e3da81b25dd0a0eb5aed7b9e0fe42b2))
* **tokens:** multiple authorities ([bc47dcf](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/bc47dcf43f08fdc6e9b52dc205fbabea1893259f))
* **tooltip:** added test warning to admin test page ([885efd3](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/885efd364b5a59365b1594e33c5de8317e9f16fd))
* **tooltips:** add auto unzip and multiFileField tooltips ([276dcb6](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/276dcb6ad962b4ffca07e58b6231cebfaceb68c6))
* **tooltips:** add option for inline tooltips ([0b2e931](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/0b2e9319be2bec3546f2af2568b86ce39f114025))
* **tooltips:** replace tooltips ([3b0e1d5](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/3b0e1d570d4000cf386def168400413fb0992753))
* **tooltips:** tooltips from messages ([f85ab69](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/f85ab69114f4947cf1b80469d3e75c2848bc8d5a))
* **tou:** add english translation ([ce8b1a6](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ce8b1a6c64a8f4f0e64f90367e23d0f85a7f1e8c))
* **tou:** first stub of german tou ([74caeca](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/74caeca9676ae1c1a00fc22e16b6b47209499d88))
* **tou:** implement Terms of Use (tou) route ([932cd5c](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/932cd5cfdba309b1c70682c0f1731ac173298432))
* **tou:** small fix in english translation ([aced70f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/aced70f8346cb9f2d4b7a58624dc865005f35a48))
* **tou:** small fixes in german version ([246af70](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/246af702d2fc898fb012d448f36b9d509371a6f1))
* **transaction-log:** more details about submission files ([b9cc5b9](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b9cc5b9970cc0b1f61c9df03c54901d9d6e822d0))
* **tutor tabel sorting:** dbt sorting tutors added ([b1787cd](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b1787cd77e8a643accc0ef54cc18c87df215680c))
* **tutorial-users:** replace study-fields column with qualifications column ([9850e1d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/9850e1dd88a5371abe67fd5fb69458d7f52ea8e8))
* **tutorial-users:** table action for granting qualifications ([fa0caba](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/fa0caba55d05f080f5ed98b0b83dde3c6cebe7b7))
* **tutorials:** delegate control to tutors ([261f3ed](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/261f3ed92f4ae689d0fbaf83a8070b619d3c2444))
* use c++ library for json parsing from database ([f226751](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/f22675189e19d1cce20362121ef8c8aebe3628f1))
* use pandoc to convert html emails to markdown (plaintext) ([4879bb8](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/4879bb840482b6b81b0dee58a01f3f33c5c1c725))
* **user-schools:** allow users to override automatic school assoc' ([7d927fd](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/7d927fdd5fe6e1e4abd315abf4b415c58f99e89b))
* **user-schools:** automatically assign users to schools ([12067de](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/12067de2ff7e645f324ba266869d4ba1ca0ad064))
* usergroups & metrics usergroup ([9204565](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/9204565cac7b2c52f5d69ada066824e37ba6ae38)), closes [#538](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/538)
* **users-add:** add error message for users not found in avs ([e273c60](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/e273c60a2325f75033d2393725ce7a25368821bf))
* **users-add:** redirect to different routes depending on tutorial ([93c6853](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/93c6853b082a5d2195bb55cbf7b792d2f4307254))
* **users-add:** upsert tutorial participants ([662445e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/662445e8cc92cf9f5815851d7e9d0b559cef289e))
* **users:** allow customisation of displayed email address ([2f38278](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/2f38278ab141ac4db7d16a4b6d990c58067b200e)), closes [#459](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/459)
* **users:** allow customisation of userDisplayName ([a85f317](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/a85f317bf2de8c5038b406d6c5601d0ead8e4bd2)), closes [#346](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/346)
* **users:** allow users to set postal address and email encryption password ([655fcf7](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/655fcf756471a2dfc6380e4b63236ca8d5229e11))
* **users:** assimilation ([ef51c6e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ef51c6e7c34effa691125e4313876d95feda96af))
* **users:** generalise UserLecturer and UserAdmin to UserFunction ([76f8da5](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/76f8da52e0f532ef08df5ad649aa3d2bb24159f5)), closes [#320](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/320)
* **users:** ldap-synchronise arbitrary subsets of users ([0789536](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/07895368ddda85cf8d1ce9838d5cfe5db32c511d))
* **users:** lecturer invitations ([e6c3be4](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/e6c3be4f7b1694420756ab194f0c607af427cfdd))
* **users:** sex ([c2a8381](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c2a83812785e9f8f2ad948a551527df95e24d118))
* **users:** store first names and titles ([ceed070](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ceed070e35f8ef4decc0afcfc338abbfdb8a46ac))
* **users:** switching between AuthModes & password changing ([0d610cc](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/0d610ccf4459ab929d18ab7285dd080b51394ad2))
* **util-registry:** ensure specific start ordering ([baf8b18](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/baf8b18dc3049559891647ab2ef43a23a982cbdb)), closes [#587](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/issues/587)
* **util-registry:** more debug info for setup util instances ([00584f9](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/00584f95901786ce211f64e1edcfeedab2299c45))
* **utils:** throwLeftWith to facilitate ldap code ([8417eb5](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/8417eb57c9b8324cee81cbd16ff72f6039757a8e))
* warnings about multiple terms/schools ([91e1bf9](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/91e1bf99966655b0a8f7ab99d0ddebe5642c627b))
* well known files ([068632b](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/068632b11753f58e8142608db3dc139d0c84f93f))
* **workflows:** add missing instances; correct Int64 workaround ([8b32ede](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/8b32edee64509ca5a3d5fc206192d4fa43cc1971))
* **workflows:** additional work on WorkflowWorkflowWorkflow ([5108e14](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/5108e1494aa8b2bc8b383a349d1d2a4e0249501f))
* **workflows:** create new workflow definitions ([4d63d30](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/4d63d306347ed452822b6bea101cdf4391363ed1))
* **workflows:** definition route stubs & i18n ([e3b5b93](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/e3b5b93c71e49203e428382cfabb3d536f290cc4))
* **workflows:** delete definitions ([bda4f81](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/bda4f81702d94d81427a4980b217be8cae2b9152))
* **workflows:** edge messages ([c22004e](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/c22004e1b2f3cd85297faaf41d76954c0625e308))
* **workflows:** enum fields ([426c40f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/426c40f0a4f596804eca723e09894f9c5606af6e))
* **workflows:** explanation text ([aba6737](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/aba673756e9e40057625c41da8b32d378e9b67c6))
* **workflows:** further work on WorkflowWorkflowWorkflow ([5b897c7](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/5b897c7a42067d6a7918dc7bc9640b5c3d8a1367))
* **workflows:** improve linter ([316097a](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/316097a07ed89e40ecbf3dd8a7160eca95bd7a67))
* **workflows:** initiate ([fd7c91f](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/fd7c91f5b8aa2645e0e072115d6a7da58323971a))
* **workflows:** list & edit definitions ([ff370c6](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ff370c68c735c492e8e588a8bb8e4055aa8cc0f4))
* **workflows:** list involved users ([d8878a9](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/d8878a905e07f1b5fb5159ecdaf70f27e9c1dc37))
* **workflows:** make admin or token sufficient for all roles ([7a7cd4d](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/7a7cd4d07c907611cf72e5ebe3ae41c3a401ef64))
* **workflows:** new field CaptureDateTime ([5944a17](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/5944a174bc8a749c60718b58d656f44cd21e7ecf))
* **workflows:** node messages ([6a7a892](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/6a7a892c74ad7da906a841fbfd031cca59174a8c))
* **workflows:** placeholder handlers ([baea302](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/baea302e48dd6c603eebba7040923f0c23266f40))
* **workflows:** prepare for admin-workflow-instance-edit ([ee6fecb](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ee6fecb79e4807beceadd15f19e41393f7707135))
* **workflows:** proper workflow-workflow-tables ([ac08846](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/ac08846c267f20fa053e3bd73bea72b224b636c6))
* **workflows:** replace pages with warning if turned off ([8634d20](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/8634d20e2ad2d3746cf7b6111b91db9e57e4863b))
* **workflows:** restrict day field wrt. current time ([b742731](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/b7427315119843e6b2cebad4f6f420d57c2efaf0))
* **workflows:** update instances from definitions ([32efdae](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/32efdae839b1a3e43ed4161d20e598964970f15e))
* **workflows:** wire up ws-school ([82b3a63](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/82b3a6364c77c64a653e927cd0242d64ffcf9d2a))
=======
* **model:** separate user authentication data from User table; add ExternalAuth and InternalAuth models
* **model:** move user authentication data to new ExternalUser model
* **settings:** rename userdb app settings
@ -1749,6 +874,7 @@ them together now)
* **model:** separate user authentication data from User table; add ExternalAuth and InternalAuth models ([54f2430](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/54f2430b3e79d3b7c396ac4cf1d4d0da860e3d02))
* **settings:** rename userdb app settings ([9f299c8](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/commit/9f299c854c9d2d2f1b1127c85a31b787f85fa210))
=======
## [27.4.79](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.78...v27.4.79) (2024-09-10)
@ -1963,6 +1089,78 @@ them together now)
* **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))
## [v27.4.59-test-g0.0.17](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-f0.0.17...v27.4.59-test-g0.0.17) (2025-02-18)
## [v27.4.59-test-f0.0.17](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-g0.0.16...v27.4.59-test-f0.0.17) (2025-02-17)
## [v27.4.59-test-g0.0.16](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-f0.0.16...v27.4.59-test-g0.0.16) (2025-02-16)
## [v27.4.59-test-f0.0.16](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-f0.0.15...v27.4.59-test-f0.0.16) (2025-02-16)
## [v27.4.59-test-f0.0.15](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-e0.0.15...v27.4.59-test-f0.0.15) (2025-02-15)
## [v27.4.59-test-e0.0.15](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-f0.0.14...v27.4.59-test-e0.0.15) (2025-02-14)
## [v27.4.59-test-f0.0.14](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-e0.0.14...v27.4.59-test-f0.0.14) (2025-02-14)
## [v27.4.59-test-e0.0.14](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-e0.0.13...v27.4.59-test-e0.0.14) (2025-02-13)
## [v27.4.59-test-e0.0.13](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-e0.0.12...v27.4.59-test-e0.0.13) (2025-02-12)
## [v27.4.59-test-e0.0.12](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-d0.0.12...v27.4.59-test-e0.0.12) (2025-02-12)
## [v27.4.59-test-d0.0.12](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-d0.0.11...v27.4.59-test-d0.0.12) (2025-02-11)
## [v27.4.59-test-d0.0.11](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-c0.0.11...v27.4.59-test-d0.0.11) (2025-02-11)
## [v27.4.59-test-c0.0.11](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-b0.0.11...v27.4.59-test-c0.0.11) (2025-02-11)
## [v27.4.59-test-b0.0.11](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-c0.0.10...v27.4.59-test-b0.0.11) (2025-02-11)
## [v27.4.59-test-c0.0.10](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-b0.0.10...v27.4.59-test-c0.0.10) (2025-02-11)
## [v27.4.59-test-b0.0.10](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-a0.0.10...v27.4.59-test-b0.0.10) (2025-02-11)
## [v27.4.59-test-a0.0.10](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-a0.0.9...v27.4.59-test-a0.0.10) (2025-02-11)
## [v27.4.59-test-a0.0.9](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-a0.0.8...v27.4.59-test-a0.0.9) (2025-02-10)
## [v27.4.59-test-a0.0.8](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-a0.0.7...v27.4.59-test-a0.0.8) (2025-02-10)
## [v27.4.59-test-a0.0.7](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-a0.0.6...v27.4.59-test-a0.0.7) (2025-02-10)
## [v27.4.59-test-a0.0.6](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-a0.0.5...v27.4.59-test-a0.0.6) (2025-02-08)
## [v27.4.59-test-a0.0.5](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-a0.0.4...v27.4.59-test-a0.0.5) (2025-02-07)
## [v27.4.59-test-a0.0.4](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-a0.0.3...v27.4.59-test-a0.0.4) (2025-02-07)
## [v27.4.59-test-a0.0.3](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-a0.0.2...v27.4.59-test-a0.0.3) (2025-02-06)
## [v27.4.59-test-a0.0.2](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-a0.0.1...v27.4.59-test-a0.0.2) (2025-02-05)
## [v27.4.59-test-a0.0.1](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59-test-a0.0.0...v27.4.59-test-a0.0.1) (2025-02-05)
### Bug Fixes
* **ghci:** ghci works now as expected ([c3117db](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive/commit/c3117dbdcd1de9ef9f0751afa45018e2ebce2c42))
## [v27.4.59-test-a0.0.0](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive//compare/v27.4.59...v27.4.59-test-a0.0.0) (2024-10-25)
### Features
* **util script:** Util script for renaming of files added. ([caf8fec](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive/commit/caf8fec5acb94df16293bf9aa0cdab766f8829e8))
* **frontend:** load icons from svg files ([22781e1](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive/commit/22781e1565e890cf6c5b40973146b0334cb667aa))
### Bug Fixes
* **stack.yaml:** move to uniworx.de gitlab ([55484e6](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive/commit/55484e631b786ea3710d322282019baf5292c243))
* **utils/renamer:** Mehr outputs nur im verbose-Fall. ([ac30cb9](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive/commit/ac30cb9e6712d0ee3f204da4863d1e2509af8a76))
* **utils:** Verboseparameter -v hinzugefuegt; rekursives makedir; genauere Meldungen. ([1806d9f](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive/commit/1806d9f01fc4a0746d2f9df42ef1ee6827c7fa09))
* **Dockerfile:** change rights of source dir to env user ([e7a8183](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive/commit/e7a8183656ae419cfee2942543045c6fa6a9caa3))
* **Makefile:** add missing dependency on well-known for backend-builds ([a09dc59](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive/commit/a09dc59f260843f8815c382576bb5254d21104bf))
* **frontend:** fixed icon colour in table headers ([4c4571d](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive/commit/4c4571d2d0879e89f2572eba6015d34a7f4794c8))
* **doc:** minor haddock problems ([d4f8a6c](https://fraport@dev.azure.com/fraport/Fahrerausbildung/_git/FRADrive/commit/d4f8a6c77b2a4a4540935f7f0beca0d0605508c8))
## [27.4.59](https://gitlab2.rz.ifi.lmu.de/uni2work/uni2work/compare/v27.4.58...v27.4.59) (2024-02-13)

View File

@ -69,13 +69,19 @@ clean:
-rm -rf .stack-work .stack-work.lock
-rm -rf bin .Dockerfile develop
-$(CONTAINER_COMMAND) container prune --force
.PHONY: clean-images
# HELP: stop all running containers and clean all images from local repositories
clean-images:
rm -rf develop
sleep 5
-$(CONTAINER_COMMAND) system prune --all --force --volumes
-$(CONTAINER_COMMAND) image prune --all --force
-$(CONTAINER_COMMAND) volume prune --force
.PHONY: clean-all
# HELP: like clean but with full container, image, and volume prune
clean-all: clean
-rm -rf .stack
-$(CONTAINER_COMMAND) system prune --all --force --volumes
-$(CONTAINER_COMMAND) image prune --all --force
-$(CONTAINER_COMMAND) volume prune --force
$(CONTAINER_COMMAND) system reset --force
.PHONY: release
# HELP: create, commit and push a new release
@ -89,16 +95,20 @@ release:
git push origin $${VERSION}
.PHONY: compile
# HELP: perform full compilation (frontend and backend)
compile:
$(MAKE) compile-frontend
$(MAKE) compile-backend
.PHONY: start
# HELP: start complete development environment with a fresh test database
start:
$(MAKE) start-postgres
$(MAKE) start-memcached
$(MAKE) start-minio
$(MAKE) start-maildev
$(MAKE) compile-frontend
$(MAKE) compile-uniworxdb
$(MAKE) start-backend
.PHONY: %-backend
@ -135,7 +145,7 @@ start:
%-postgres: SERVICE=postgres
%-postgres: SERVICE_VARIANT=postgres
%-postgres: BASE_PORTS = "PGPORT=5432"
%-postgres: IMAGE=localhost/fradrive/postgres
%-postgres: SET_IMAGE=localhost/fradrive/postgres
.PHONY: %-memcached
%-memcached: SERVICE=memcached
@ -143,6 +153,18 @@ start:
%-memcached: SET_IMAGE=$$(MEMCACHED_IMAGE) --port=`cat $$(CONTAINER_FILE) | grep 'MEMCACHED_PORT=' | sed 's/MEMCACHED_PORT=//'`
%-memcached: BASE_PORTS = "MEMCACHED_PORT=11211"
.PHONY: %-maildev
%-maildev: SERVICE=maildev
%-maildev: SERVICE_VARIANT=maildev
%-maildev: SET_IMAGE=$$(MAILDEV_IMAGE) --port=`cat $$(CONTAINER_FILE) | grep 'MAILDEV_PORT=' | sed 's/MAILDEV_PORT=//'`
%-maildev: BASE_PORTS = "MAILDEV_PORT=1025"
.PHONY: %-release
%-release: PROD=true
%-release: SERVICE=fradrive
%-release: SERVICE_VARIANT=fradrive
%-release: IMAGE=localhost/fradrive/fradrive
.PHONY: %-minio
%-minio: SERVICE=minio
%-minio: SERVICE_VARIANT=minio
@ -234,6 +256,7 @@ endif
IMAGE="$(SET_IMAGE)" ; \
else \
IMAGE=$(IMAGE) ; \
MAKECALL="make -- --$(JOB)-$(SERVICE_VARIANT) IN_CONTAINER=true" ; \
fi ; \
CONTAINER_ID=`$(CONTAINER_BGRUN) \
-v $(PWD):$(PROJECT_DIR):rw \
@ -244,7 +267,7 @@ endif
--env SRC=$(SRC) \
--name $${CONTAINER_NAME} \
$${IMAGE} \
make -- --$(JOB)-$(SERVICE_VARIANT) IN_CONTAINER=true \
$${MAKECALL} \
` ; \
printf "CONTAINER_ID=$${CONTAINER_ID}" >> "$(CONTAINER_FILE)" ; \
if [[ "true" == "$(CONTAINER_ATTACHED)" ]] ; then \
@ -253,7 +276,7 @@ endif
# For Reverse Proxy Problem see: https://groups.google.com/g/yesodweb/c/2EO53kSOuy0/m/Lw6tq2VYat4J
# HELP(start-backend): start development instance
--start-backend:
--start-backend: --dependencies-backend
export YESOD_IP_FROM_HEADER=true; \
export DEV_PORT_HTTP=`cat $(CONTAINER_FILE) | grep 'DEV_PORT_HTTP=' | sed 's/DEV_PORT_HTTP=//'`; \
export DEV_PORT_HTTPS=`cat $(CONTAINER_FILE) | grep 'DEV_PORT_HTTPS=' | sed 's/DEV_PORT_HTTPS=//'`; \
@ -273,11 +296,12 @@ endif
export AVSPASS=$${AVSPASS:-nopasswordset} ; \
stack $(STACK_CORES) exec --local-bin-path $$(pwd)/bin --copy-bins -- yesod devel -p "$${DEV_PORT_HTTP}" -q "$${DEV_PORT_HTTPS}"
# HELP(compile-backend): compile backend binaries
--compile-backend:
--compile-backend: --dependencies-backend
stack build $(STACK_CORES) --fast --profile --library-profiling --executable-profiling --flag uniworx:-library-only $(--DEVELOPMENT) --local-bin-path $$(pwd)/bin --copy-bins
# HELP(dependencies-backend): (re-)build backend dependencies
--dependencies-backend: #uniworx.cabal
chown -R `id -un`:`id -gn` "$(PROJECT_DIR)"; \
stack install hpack; stack install yesod-bin; \
stack build -j2 --only-dependencies
# HELP(lint-backend): lint backend
--lint-backend:
@ -289,10 +313,10 @@ endif
# stack exec -- hpack --force
# HELP(compile-frontend): compile frontend assets
--compile-frontend: node_modules assets esbuild.config.mjs frontend/src/env.sass
--compile-frontend: --dependencies-frontend
npm run build
--start-frontend: --compile-frontend;
--dependencies-frontend: node_modules assets;
--dependencies-frontend: node_modules assets esbuild.config.mjs frontend/src/env.sass;
node_modules: package.json package-lock.json
npm install --cache .npm --prefer-offline
package-lock.json: package.json
@ -306,7 +330,7 @@ assets/icons: node_modules assets/icons-src/fontawesome.json
-cp assets/icons-src/*.svg assets/icons/fradrive
frontend/src/env.sass:
echo "\$$path: '$${PROJECT_DIR}'" > frontend/src/env.sass
static: node_modules assets esbuild.config.mjs frontend/src/env.sass
static: --dependencies-frontend
npm run build
well-known: static;
--lint-frontend: --compile-frontend

View File

@ -29,6 +29,7 @@
"file-upload": "file-arrow-up",
"file-zip": "file-zipper",
"file-csv": "file-csv",
"file-missing": "file-circle-minus",
"sft-question": "circle-question",
"sft-hint": "life-ring",
"sft-solution": "circle-exclamation",
@ -76,12 +77,13 @@
"submission-no-users": "user-slash",
"reset": "arrow-rotate-left",
"blocked": "ban",
"certificate": "certificate",
"certificate": "car-side",
"print-center": "envelopes-bulk",
"letter": "envelopes-bulk",
"at": "at",
"supervisor": "person",
"supervisor-foreign": "person-rays",
"superior": "user-tie",
"waiting-for-user": "user-gear",
"expired": "hourglass-end",
"locked": "lock",
@ -89,9 +91,18 @@
"trash": "trash",
"reset-tries": "trash-can-arrow-up",
"company": "building",
"company-warning": "building-circle-exclamation",
"edit": "pen-to-square",
"user-edit": "user-pen",
"loading": "spinner",
"placeholder": "notdef"
"placeholder": "notdef",
"reroute": "diamond-turn-right",
"top": "award",
"wildcard": "asterisk",
"user-unknown": "user-slash",
"user-badge": "id-badge",
"glasses": "glasses",
"missing": "question",
"pin-protect": "key"
}

View File

@ -0,0 +1,32 @@
# SPDX-FileCopyrightText: 2024 Steffen Jost <s.jost@fraport.de>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
# Values formatted like "_env:ENV_VAR_NAME:default_value" can be overridden by the specified environment variable.
# See https://github.com/yesodweb/yesod/wiki/Configuration#overriding-configuration-values-with-environment-variables
# NB: If you need a numeric value (e.g. 123) to parse as a String, wrap it in single quotes (e.g. "_env:PGPASS:'123'")
# See https://github.com/yesodweb/yesod/wiki/Configuration#parsing-numeric-values-as-strings
# DEVELOPMENT ONLY, NOT TO BE USED IN PRODUCTION
avs-licence-synch:
times: [12]
level: 4
reason-filter: "(firm|block)"
max-changes: 999
mail-reroute-to:
name: "FRADrive-QA-Umleitungen"
email: "FRADrive-TEST-Umleitungen@fraport.de"
# Enqueue at specified hour, a few minutes later
job-lms-qualifications-enqueue-hour: 16
job-lms-qualifications-dequeue-hour: 4
# Using these setting kills the job-workers somehow
# job-workers: 5
# job-flush-interval: 600
# job-stale-threshold: 3600
# job-move-threshold: 60

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>, 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-2025 Sarah Vaupel <sarah.vaupel@uniworx.de>, 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
@ -91,10 +91,6 @@ synchronise-avs-users-interval: "_env:SYNCHRONISE_AVS_INTERVAL:21600" # alle 6
study-features-recache-relevance-within: 172800
study-features-recache-relevance-interval: 293
# Enqueue at specified hour, a few minutes later
job-lms-qualifications-enqueue-hour: 16
job-lms-qualifications-dequeue-hour: 4
log-settings:
detailed: "_env:DETAILED_LOGGING:false"
all: "_env:LOG_ALL:false"
@ -173,12 +169,14 @@ user-sync-within: "_env:USER_SYNC_WITHIN:1209600" # 14 Tage in Sekunden
user-sync-interval: "_env:USER_SYNC_INTERVAL:3600" # jede Stunde
lms-direct:
upload-header: "_env:LMSUPLOADHEADER:true"
upload-delimiter: "_env:LMSUPLOADDELIMITER:"
download-header: "_env:LMSDOWNLOADHEADER:true"
download-delimiter: "_env:LMSDOWNLOADDELIMITER:,"
download-cr-lf: "_env:LMSDOWNLOADCRLF:true"
deletion-days: "_env:LMSDELETIONDAYS:7"
upload-header: "_env:LMSUPLOADHEADER:true"
upload-delimiter: "_env:LMSUPLOADDELIMITER:"
download-header: "_env:LMSDOWNLOADHEADER:true"
download-delimiter: "_env:LMSDOWNLOADDELIMITER:,"
download-cr-lf: "_env:LMSDOWNLOADCRLF:true"
orphan-deletion-days: "_env:LMSORPHANDELETIONDAYS:33"
orphan-deletion-batch: "_env:LMSORPHANDELETIONBATCH:12"
orphan-deletion-repeat-hours: "_env:LMSORPHANDELETIONREPEATHOURS:24"
avs:
host: "_env:AVSHOST:skytest.fra.fraport.de"
@ -233,9 +231,6 @@ memcached:
timeout: "_env:MEMCACHED_TIMEOUT:20"
expiration: "_env:MEMCACHED_EXPIRATION:300"
memcache-auth: true
memcached-local:
maximum-ghost: 512
maximum-weight: 104857600 # 100MiB
upload-cache:
host: "_env:UPLOAD_S3_HOST:localhost" # should be optional, but all file transfers will be empty without an S3 cache
@ -347,17 +342,6 @@ fallback-personalised-sheet-files-keys-expire: 2419200
download-token-expire: 604801
file-source-arc:
maximum-ghost: 512
maximum-weight: 1073741824 # 1GiB
file-source-prewarm:
maximum-weight: 1073741824 # 1GiB
start: 1800 # 30m
end: 600 # 10m
inhibit: 3600 # 60m
steps: 20
max-speedup: 3
bot-mitigations:
- only-logged-in-table-sorting
- unauthorized-form-honeypots

View File

@ -16,5 +16,4 @@ log-settings:
auth-dummy-login: true
server-session-acid-fallback: true
job-cron-interval: null
job-workers: 1
job-workers: 20

View File

@ -17,22 +17,45 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
apt-get -y update && apt-get install -y --no-install-recommends locales locales-all
# run-time dependencies for uniworx binary
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get -y update && apt-get -y install fonts-roboto
# RUN apt-get -y update && apt-get -y install pdftk
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get -y update && apt-get -y install texlive-latex-recommended texlive-luatex texlive-plain-generic texlive-lang-german texlive-lang-english
# RUN apt-get -y update && apt-get -y install \
# texlive texlive-latex-recommended texlive-luatex texlive-plain-generic texlive-lang-german texlive-lang-english
RUN apt-get -y update && apt-get -y install \
wget \
perl \
xz-utils \
fonts-roboto \
texlive \
texlive-luatex \
texlive-latex-extra \
texlive-fonts-recommended \
texlive-fonts-extra \
&& apt-get clean
# RUN ls /usr/local/texlive
# RUN chown -hR root /usr/local/texlive/2018
ENV PATH="/usr/local/texlive/2018/bin/x86_64-linux:${PATH}"
ENV TEXLIVE_VERSION=2018
RUN tlmgr init-usertree
RUN tlmgr option repository ftp://tug.org/historic/systems/texlive/2018/tlnet-final
RUN tlmgr update --self --all
RUN tlmgr install \
babel \
babel-english \
babel-german \
booktabs \
textpos \
enumitem \
# luatex lualatex luatexbase lualatex-math eurosym \
# above line requires tlmgr to run in -sys mode (~root?! apparently -privileged is missing)
koma-script \
unicode-math \
selnolig
ARG PROJECT_DIR=/fradrive
ENV PROJECT_DIR=${PROJECT_DIR}
RUN mkdir -p "${PROJECT_DIR}"; chmod -R 7777 "${PROJECT_DIR}"
# RUN mkdir -p "${PROJECT_DIR}"; chmod -R 777 "${PROJECT_DIR}"
WORKDIR ${PROJECT_DIR}
ENV HOME=${PROJECT_DIR}
ENV STACK_ROOT="${PROJECT_DIR}/.stack"
RUN if [ ! -z "${IN_CI}" ]; then \
stack install yesod-bin; \
stack install hpack; \
fi
ENV STACK_ROOT="${PROJECT_DIR}/.stack"

View File

@ -14,10 +14,12 @@ ENV DEBIAN_FRONTEND=noninteractive
ENV TZ=Etc/UTC
RUN apt-get update && apt-get -y install libpq-dev
RUN apt-get update && apt-get -y install libsodium-dev
RUN apt-get update && apt-get -y install fonts-roboto
# TODO: minimize texlive dependencies, switch to basic schemes where possible
RUN apt-get update && apt-get -y install \
texlive-latex-base \
texlive-full \
texlive-luatex \
texlive-plain-generic \
texlive-fonts-recommended \
texlive-fonts-extra \
texlive-lang-english \

View File

@ -1,6 +0,0 @@
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

View File

@ -5,7 +5,7 @@
@import 'env';
$ico-width: 30px;
$ico-width: 15px;
$icons: new,
ok,
@ -35,6 +35,7 @@ $icons: new,
file-upload,
file-zip,
file-csv,
file-missing,
sft-question,
sft-hint,
sft-solution,
@ -95,9 +96,15 @@ $icons: new,
trash,
reset-tries,
company,
company-warning,
edit,
user-edit,
placeholder,
glasses,
user-badge,
user-unknown,
missing,
pin-protect,
loading;
@ -133,6 +140,7 @@ $icons: new,
.large-ico {
font-size: 2em;
min-width: 1em;
}
.ico-spin {

View File

@ -96,7 +96,7 @@ sampleIntegral = sampleN scaleIntegral
instance PathPiece DiffTime where
toPathPiece = (toPathPiece :: Pico -> Text) . MkFixed . diffTimeToPicoseconds
fromPathPiece t = fromPathPiece t <&> \(MkFixed ps :: Pico) -> picosecondsToDiffTime ps
data LoadSimulation
= LoadSheetDownload
@ -214,13 +214,13 @@ runSimulation sim = do
delays <- replicateM (fromIntegral p) $ do
d <- view $ _2 . _simDelay
sampleNDiffTime d
forConcurrently_ ([1..p] `zip` sort delays) $ \(n, d') -> do
begin <- liftIO getCurrentTime
dur <- view $ _2 . _simDuration
tDuration <- sampleNDiffTime dur
let MkFixed us = realToFrac d' :: Micro
threadDelay $ fromInteger us
start <- liftIO getCurrentTime
@ -268,7 +268,7 @@ runSimulation' LoadSheetSubmission = do
-- Just formData <- return . getFormData FIDsubmission $ resp ^. responseBody
-- Just addButtonData <- return . flip (runFormScraper FIDsubmission) (resp ^. responseBody) $ do
-- let btnSel = "button" Scalpel.@: [Scalpel.hasClass "btn-mass-input-add"]
-- name <- Scalpel.attr "name" btnSel
-- value <- Scalpel.attr "value" btnSel
-- guard $ value == "add__0__0"
@ -305,7 +305,7 @@ runSimulation' LoadSheetSubmission = do
procEnd <- join $ asks runtime
print ("proc", procEnd - procStart)
resp3 <- liftIO . httpRetry $ Session.post session (uriToString id formURI mempty) subData
void . evaluate $! resp3
where
@ -328,11 +328,11 @@ runSimulation' LoadSheetSubmission = do
-> m ()
logRetry shouldRetry err status = liftIO . putStrLn . pack $ Retry.defaultLogMsg shouldRetry err status
-- runSimulation' other = terror $ "Not implemented: " <> tshow other
runFormScraper :: FormIdentifier -> Scalpel.Scraper Lazy.ByteString a -> Lazy.ByteString -> Maybe a
runFormScraper fid innerS = fmap join . flip Scalpel.scrapeStringLike $
runFormScraper fid innerS = fmap join . flip Scalpel.scrapeStringLike $
fmap listToMaybe . Scalpel.chroots "form" $ do
fid' <- Scalpel.attr "value" $ "input" Scalpel.@: ["name" Scalpel.@= "form-identifier"]
guard $ fid' == encodeUtf8 (fromStrict $ toPathPiece fid)
@ -341,11 +341,11 @@ runFormScraper fid innerS = fmap join . flip Scalpel.scrapeStringLike $
getFormData :: FormIdentifier -> Lazy.ByteString -> Maybe [FormParam]
getFormData = flip runFormScraper $
Scalpel.chroots ("input") $ do
Scalpel.chroots "input" $ do
name <- Scalpel.attr "name" Scalpel.anySelector
value <- Scalpel.attr "value" Scalpel.anySelector <|> pure ""
return $ toStrict name := value
newLoadSession :: ReaderT SimulationContext IO Session
newLoadSession = do
@ -354,7 +354,7 @@ newLoadSession = do
let withToken = case loadToken of
Nothing -> id
Just (Jwt bs) -> (:) (hAuthorization, "Bearer " <> bs) . filter ((/= hAuthorization) . fst)
liftIO . Session.newSessionControl (Just mempty) $ tlsManagerSettings
{ managerModifyRequest = \req -> return $ req { requestHeaders = withToken $ requestHeaders req }

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>,Gregor Kleen <gregor.kleen@ifi.lmu.de>,Winnie Ros <winnie.ros@campus.lmu.de>,Steffen Jost <s.jost@fraport.de>
# SPDX-FileCopyrightText: 2022-2025 Sarah Vaupel <sarah.vaupel@uniworx.de>,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
@ -109,9 +109,10 @@ ProblemsDriverSynch1up: Alle gültigen Vorfeld-Fahrberechtigungen 'F' sind im AV
ProblemsDriverSynch2: Alle gültigen Rollfeld-Fahrberechtigungen 'R' sind im AVS eingetragen
ProblemsRDriversHaveFs: Alle Inhaber einer Rollfeld-Fahrberechtigung besitzen auch eine gültige Vorfeld-Fahrberechtigung
ProblemsDriversHaveAvsIds: Alle Inhaber einer Fahrberechtigung konnten einer AVS Identifikationsnummer zugeordnet werden
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 #{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
ProblemsNoInsaneCompanySupervisions: Sind alle Firmen-bezogenen Ansprechpartnerbeziehungen zwischen passenden Firmenangehörigen?
ProblemsUnreachableHeading: Unerreichbare Benutzer
ProblemsUnreachableBody: Benutzer ohne E-Mail oder Postadresse, welche z.B. bei ablaufenden Berechtigungen nicht benachrichtigt werden können:
ProblemsUnreachableButtons: Synchronisation für Unerreichbare starten
@ -123,6 +124,7 @@ ProblemsAvsSynchHeading: Synchronisation AVS Fahrberechtigungen
ProblemsAvsErrorHeading: Fehlermeldungen
ProblemsInterfaceSince: Berücksichtigt werden nur Erfolge und Fehler seit
ProblemAvsUsrHadR: Momentan gültiges R im AVS
ProblemLastCheckTime t@Text: Letzte Prüfung vor #{t}
AdminProblemSolved: Erledigt
AdminProblemSolver: Bearbeitet von

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>,Winnie Ros <winnie.ros@campus.lmu.de>,Steffen Jost <s.jost@fraport.de>
# SPDX-FileCopyrightText: 2022-2025 Sarah Vaupel <sarah.vaupel@uniworx.de>,Winnie Ros <winnie.ros@campus.lmu.de>,Steffen Jost <s.jost@fraport.de>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
@ -112,6 +112,7 @@ ProblemsDriversHaveAvsIds: All driving licence holder could be matched with thei
ProblemsUsersAreReachable: Either Email or postal address is known for all users
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
ProblemsNoInsaneCompanySupervisions: All company related supervisions are between company-associated users
ProblemsUnreachableHeading: Unreachable Users
ProblemsUnreachableBody: Users without Email nor postal address, who thus cannot be notified about expiring qualifications:
ProblemsUnreachableButtons: Start synchronisation for unreachable users only
@ -123,6 +124,7 @@ ProblemsAvsSynchHeading: Synchronisation AVS Driving Licences
ProblemsAvsErrorHeading: Error Log
ProblemsInterfaceSince: Only considering successes and errors since
ProblemAvsUsrHadR: Currenlt R valid in AVS
ProblemLastCheckTime t: Last checked #{t} ago
AdminProblemSolved: Done
AdminProblemSolver: Solved by

View File

@ -26,30 +26,30 @@ UnauthorizedSiteAdmin: You are no system-wide administrator.
UnauthorizedSchoolAdmin: You are no administrator for this department.
UnauthorizedAdminEscalation: You aren't an administrator for all departments for which this user is an administrator.
UnauthorizedExamOffice: You are not part of an exam office.
UnauthorizedEvaluation: You are not charged with course type evaluation.
UnauthorizedEvaluation: You are not charged with course category evaluation.
UnauthorizedExamExamOffice: You are not part of the appropriate exam office for any of the participants of this exam.
UnauthorizedSchoolExamOffice: You are not part of an exam office for this school.
UnauthorizedSystemExamOffice: You are not charged with system wide exam administration.
UnauthorizedSystemPrinter: You are not charged with system wide letter printing.
UnauthorizedExternalExamExamOffice: You are not part of the appropriate exam office for any of the participants of this exam.
UnauthorizedSchoolLecturer: You are no course administrator for this department.
UnauthorizedLecturer: You are no administrator for this course type.
UnauthorizedCorrector: You are no sheet corrector for this course type.
UnauthorizedLecturer: You are no administrator for this course category.
UnauthorizedCorrector: You are no sheet corrector for this course category.
UnauthorizedSheetCorrector: You are no corrector for this sheet.
UnauthorizedExamCorrector: You are no corrector for this exam.
UnauthorizedCorrectorAny: You are no corrector for any course type.
UnauthorizedRegistered: You are no participant in this course type.
UnauthorizedCorrectorAny: You are no corrector for any course category.
UnauthorizedRegistered: You are no participant in this course category.
UnauthorizedRegisteredExam: You are not registered for this exam.
UnauthorizedRegisteredAnyExam: You are not registered for an exam.
UnauthorizedExamResult: You have no results in this exam.
UnauthorizedExamOccurrenceRegistration: Registration for exam is not done including occurrence/room.
UnauthorizedExternalExamResult: You have no results in this exam.
UnauthorizedParticipant: The specified user is no participant of this course type.
UnauthorizedParticipantSelf: You are no participant of this course type.
UnauthorizedCourseTime: This course type is not currently available.
UnauthorizedCourseRegistrationTime: This course type does not currently allow enrollment.
UnauthorizedParticipant: The specified user is no participant of this course category.
UnauthorizedParticipantSelf: You are no participant of this course category.
UnauthorizedCourseTime: This course category is not currently available.
UnauthorizedCourseRegistrationTime: This course category does not currently allow enrollment.
UnauthorizedSheetTime: This sheet is not currently available.
UnauthorizedMaterialTime: This course type material is not currently available.
UnauthorizedMaterialTime: This course category material is not currently available.
UnauthorizedTutorialTime: This course does not currently allow registration.
UnauthorizedCourseNewsTime: This news item is not currently available.
UnauthorizedExamTime: This exam is not currently available.
@ -61,7 +61,7 @@ UnauthorizedUserSubmission: Users may not directly submit for this exercise shee
UnauthorizedCorrectorSubmission: Correctors may not create submissions for this exercise sheet.
UnauthorizedCorrectionAnonymous: Correction is not anonymised.
DeprecatedRoute: This view is deprecated and will be removed.
UnfreeMaterials: Course type material are not publicly accessable.
UnfreeMaterials: Course category material are not publicly accessable.
UnauthorizedWrite: You do not have the write permission necessary to perform this action
UnauthorizedSystemMessageTime: This system-message is not currently available.
UnauthorizedSystemMessageAuth: This system-message is only available to logged in users.
@ -94,7 +94,7 @@ WorkflowRoleAlreadyInitiated: This workflow was already initiated
WorkflowRoleNoSuchWorkflowWorkflow: The given workflow could not be found
WorkflowRoleNoPayload: This workflow does not contain any data
CourseNoCapacity: Course type has reached maximum capacity
CourseNoCapacity: Course category has reached maximum capacity
TutorialNoCapacity: Course has reached maximum capacity
ExamOccurrenceNoCapacity: Occurrence/Room has reached maximum capacity
CourseNotEmpty: There are currently no participants enrolled for this course.

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 Steffen Jost <jost@tcs.ifi.lmu.de>
# SPDX-FileCopyrightText: 2022-25 Steffen Jost <s.jost@fraport.de>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
AvsPersonInfo: AVS Personendaten
@ -54,10 +54,14 @@ 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
AvsPersonSearchEmpty: Suche im AVS lieferte kein Ergebnis
AvsPersonSearchAmbiguous: Suche im AVS 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.
AvsCardsEmpty: Suche im AVS lieferte keinerlei Ausweiskarten
AvsCurrentData: Alle angezeigte Daten wurden kürzlich direkt über die AVS Schnittstelle abgerufen.
AvsUpdateDayCheck: Zusätzlich wird im Hintergrund ein AVS Datenabgleich für alle in der Tagesansicht vorkommenden Personen angestoßen (einmal pro Tag).
AvsNoApronCard: Kein gültiger Ausweis mit Vorfeld-Zugang vorhanden
AvsNoCompanyCard mcn@(Maybe CompanyName): Für buchende Firma #{maybeEmpty mcn ciOriginal} liegt kein gültiger Ausweis vor

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 Steffen Jost <jost@tcs.ifi.lmu.de>
# SPDX-FileCopyrightText: 2022-25 Steffen Jost <s.jost@fraport.de>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
AvsPersonInfo: AVS person info
@ -61,4 +61,8 @@ AvsSetLicencesFailed reason: Set driving licence within AVS failed. Reason: #{re
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.
AvsCurrentData: All shown data has been recently received via the AVS interface.
AvsUpdateDayCheck: In addition, a background AVS update has been scheduled for all persons occrring within the day agenda (once per Day).
AvsNoApronCard: No valid card granting apron access found
AvsNoCompanyCard mcn@(Maybe CompanyName): No valid card for booking company #{maybeEmpty mcn ciOriginal} found

View File

@ -70,9 +70,9 @@ CourseInvalidInput: Eingaben bitte korrigieren.
CourseEditTitle: Kursart editieren/anlegen
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.
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
CourseEditQualificationFail: Eine Qualifikation konnte uas unbekanntem Grund nicht mit diesem Kurs assoziiert werden.
CourseEditQualificationFailRights qsh@QualificationShorthand ssh@SchoolId: Qualifikation #{qsh} konnte nicht mit diesem Kurs assoziiert werden, da Ihre Berechtigungen für Bereich #{ssh} dazu nicht ausreichen.
CourseEditQualificationFailExists: Diese Qualifikation ist bereits assoziiert
CourseEditQualificationFailOrder: Diese Sortierpriorität existiert bereits
CourseLecturer: Kursverwalter:in
MailSubjectParticipantInvitation tid@TermId ssh@SchoolId csh@CourseShorthand: [#{tid}-#{ssh}-#{csh}] Einladung zur Kursartteilnahme
@ -94,6 +94,8 @@ CourseParticipantsRegisterTutorialFieldTip: Ist aktuell keine Kurs mit diesem Na
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
CourseParticipantsRegisterTutorialFirstDayTip: Wenn ein neuer Kurs gemäß einer Vorlage erstellt wird, werden die Zeiten gemäß dem Starttag angepasst
CourseParticipantsTutorialType: Typ der Vorlage
CourseParticipantsTutorialTypeTooltip: Ein neuer Kurs wird wie ein Kurs namens "Vorlage_[typ]" erstellt, wobei zuerst in der aktuellen Kursart, danach in Kursarten gleichen Namens und möglichst neuem Datum gesucht wird.
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
@ -135,6 +137,9 @@ CourseUserTutorialsDeregistered count@Int64: Teilnehmer:in von #{show count} #{p
CourseUserNoTutorialsDeregistered: Teilnehmer:in ist zu keinem der gewählten Kurse angemeldet
CourseUserTutorials: Angemeldete Kurse
CourseUserExams: Angemeldete Prüfungen
CourseUserExamOccurrences: Prüfungstermin
CourseUserExamOccurrenceOverride: Ggf. vorhandenen Prüfungstermin überschreiben
CourseUserExamOccurrenceAgainExaminer: Ggf. vorherige Prüfer erneut erlauben
CourseUserSheets: Übungsblätter
CsvColumnUserName: Voller Name des/der Teilnehmers/Teilnehmerin
CsvColumnUserMatriculation: AVS Nummer des/der Teilnehmers/Teilnehmerin
@ -239,7 +244,7 @@ UtilEditedBy name@Text time@Text: #{time} durch #{name}
CourseDate: Datum
MailSubjectLecturerInvitation tid@TermId ssh@SchoolId csh@CourseShorthand: [#{tid}-#{ssh}-#{csh}] Einladung als Kursverwalter:in
LecturerInvitationAccepted lType@Text csh@CourseShorthand: Sie wurden als #{lType} für #{csh} eingetragen
CourseExamRegistrationTime: Angemeldet seit
CourseExamRegistrationTime: Angemeldet am
CourseParticipantStateIsActiveFilter: Ansicht
CourseApply: Zur Kursart bewerben
CourseAdministrator: Kursadministrator:in

View File

@ -2,7 +2,7 @@
#
# SPDX-License-Identifier: AGPL-3.0-or-later
FilterCourse: Course
FilterCourse: Course category
FilterCourseShort: Shorthand
FilterTerm: Year
FilterCourseSchoolShort: Department
@ -15,7 +15,7 @@ FilterCourseRegisterOpen: Enrolment is allowed
CourseRegistered: Enrolled
CourseRegistration: Enrolment
CourseDescription: Description
CommCourseHeading: Course type message
CommCourseHeading: Course category message
CourseLecturers: Course administrators
CourseLecturerEmail: Email
CourseLecturerAlreadyAdded: This user is already configured as a course administrator
@ -25,12 +25,12 @@ CourseLecturerRightsIdentical: All sorts of course administrators have the same
CourseAcceptSubstitutesUntil: Accept substitute registrations until
CourseAcceptSubstitutesUntilTip: Until which time should substitute registrations through the central allocation be accepted to fill free places in the course? If left empty no substitute registrations will be made. This deadline should not arbitrarily be set early or ommitted so as to not be an unneccesarily restrictive for students. For a seminar a valid choice might be a few hours before the first meeting in which topics will be assigned.
CourseDeregisterNoShow: Record “no show” when deregistering
CourseDeregisterNoShowTip: Should “no show” be recorded as the exam achievement for all exams associated with this course type automatically whenever a course type participant deregisters themselves? This would be done once upon deregistration (if no other achievement exists for the given exam) and automatically whenever a new exam is created.
CourseDeregisterNoShowTip: Should “no show” be recorded as the exam achievement for all exams associated with this course category automatically whenever a course category participant deregisters themselves? This would be done once upon deregistration (if no other achievement exists for the given exam) and automatically whenever a new exam is created.
CourseSchool: Department
CourseSchoolMultipleTip: You may select from among multiple departments. Please ensure that you select the appropriate department for your course.
CourseName: Title
CourseShorthand: Shorthand
CourseShorthandUnique: Needs to be unique within school and year. Will be used verbatim within the url of the course type page.
CourseShorthandUnique: Needs to be unique within school and year. Will be used verbatim within the url of the course category page.
CourseSemester: Year
CourseDescriptionPlaceholder: Please include the module description
CourseHomepageExternalPlaceholder: Optional external URL
@ -38,14 +38,14 @@ CourseHomepageExternal: External homepage
CourseSemesterMultipleTip: You are currently allowed to select from among multiple years. Please ensure that you select the appropriate year for your course.
CourseVisibleFrom: Visible from
CourseVisibleTo: Visible to
CourseVisibleFromTip: The course type will be visible to others from this date onward. When left empty the course type will never be visible to other users. This does not affect course administrators, assistants, instructors, correctors, enrolled participants and applicants of/to this course. If the course type participates in a central allocation, the course type visibility will be forced during the application phase.
CourseVisibleToTip: Other users will be able to see the course type from "Visible From" up to this date. When left empty visible courses will remain visible indefinitely.
CourseMaterialFree: Course type material is publicly accessible
CourseVisibleFromTip: The course category will be visible to others from this date onward. When left empty the course category will never be visible to other users. This does not affect course administrators, assistants, instructors, correctors, enrolled participants and applicants of/to this course. If the course category participates in a central allocation, the course category visibility will be forced during the application phase.
CourseVisibleToTip: Other users will be able to see the course category from "Visible From" up to this date. When left empty visible courses will remain visible indefinitely.
CourseMaterialFree: Course category material is publicly accessible
CourseFormSectionRegistration: Registration
CourseFormSectionAdministration: Administration
CourseCapacity: Capacity
CourseCapacityTip: Maximum permissable number of enrolments for this course; leave empty for unlimited capacity
CourseSecretTip: Enrollment for this course type will require the password, if set
CourseSecretTip: Enrollment for this course category will require the password, if set
CourseSecretFormat: Arbitrary string
CourseSecretWrong: Wrong password
CourseSecret: Access password
@ -59,17 +59,17 @@ CourseVisibilityEndMustBeAfterStart: The end of the visibility period must be af
CourseRegistrationEndMustBeAfterStart: The end of the registration period must be after its start
CourseDeregistrationEndMustBeAfterStart: The end of the deregistration period must be after the start of the registration period
CourseUserMustBeLecturer: The current user needs to be a course administrator
CourseShorthandTooLong: Long course type shorthands may lead to display issues and might complicate communication with students. Please choose a more concise shorthand if possible.
CourseNotAlwaysVisibleDuringRegistration: To allow for students to register, the course type should also be visible during the entire registration period (which is currently not the case).
CourseShorthandTooLong: Long course category shorthands may lead to display issues and might complicate communication with students. Please choose a more concise shorthand if possible.
CourseNotAlwaysVisibleDuringRegistration: To allow for students to register, the course category should also be visible during the entire registration period (which is currently not the case).
NoSuchTerm tid: Year #{tid} does not exist.
NoSuchSchool ssh: Department #{ssh} does not exist.
NoSuchCourseShorthand csh: There is no course type with shorthand #{csh}.
NoSuchCourse: No such course type found.
CourseNewDupShort tid ssh csh: Could not create course type #{tid}-#{ssh}-#{csh}. Another course type with the same shorthand or title already exists for the given year and school.
NoSuchCourseShorthand csh: There is no course category with shorthand #{csh}.
NoSuchCourse: No such course category found.
CourseNewDupShort tid ssh csh: Could not create course category #{tid}-#{ssh}-#{csh}. Another course category with the same shorthand or title already exists for the given year and school.
CourseInvalidInput: Invalid input
CourseEditTitle: Edit/Create course
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.
CourseEditOk tid ssh csh: Successfully edited course category #{tid}-#{ssh}-#{csh}
CourseEditDupShort tid ssh csh: Could not edit course category #{tid}-#{ssh}-#{csh}. Another course category 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
@ -83,21 +83,23 @@ CourseParticipantInvitationAccepted courseName: You were enrolled in #{courseNam
CourseParticipantEnlistDirectly: Enrol known users directly
CourseSubmissionGroup: Registered submission group
SubmissionGroupEmptyIsUnsetTip: Leave empty to remove users from their respective submission groups
CourseParticipantsRegisterHeading: Add course type participants
CourseParticipantsRegisterActionAddParticipants: Add course type participants
CourseParticipantsRegisterHeading: Add course category participants
CourseParticipantsRegisterActionAddParticipants: Add course category participants
CourseParticipantsRegisterActionAddTutorialMembers: Add course participants
CourseParticipantsRegisterUsersField: Persons to register for course
CourseParticipantsRegisterUsersFieldTip: Please enter id card no (including dot), Fraport personnel number or email. Please separate multiple entries with comma or space.
CourseParticipantsRegisterTutorialOption: Register course type participants for course?
CourseParticipantsRegisterTutorialOption: Register course category participants for course?
CourseParticipantsRegisterTutorialField: Course
CourseParticipantsRegisterTutorialFieldTip: If there is no course with this name, a new one will be created. If there is a course with this name, the course type participants will be registered for it.
CourseParticipantsRegisterTutorialFieldTip: If there is no course with this name, a new one will be created. If there is a course with this name, the course category participants will be registered for it.
CourseParticipantsRegisterNoneGiven: No persons given to register!
CourseParticipantsRegisterNotFoundInAvs n: For #{n} #{pluralEN n "entry no corresponding person" "entries no corresponding persons"} could be found in AVS
CourseParticipantsRegisterTutorialFirstDayTip: If a new course is created and a template exists, its dates are adjusted according to the start date
CourseParticipantsRegisterUnnecessary: All requested registrations have already been saved. No actions have been performed.
CourseParticipantsTutorialType: Template type
CourseParticipantsTutorialTypeTooltip: A new course creation copies a course named "Template_[typ]", preferably from the same course category or another having the same name, the most recent being preferred.
CourseParticipantsInvited n: #{n} #{pluralEN n "invitation" "invitations"} sent via email
CourseParticipantsAlreadyRegistered n: #{n} #{pluralEN n "participant is" "participants are"} already course type #{pluralEN n "member" "members"}
CourseParticipantsAlreadyRegistered n: #{n} #{pluralEN n "participant is" "participants are"} already course category #{pluralEN n "member" "members"}
CourseParticipantsAlreadyTutorialMember n: #{n} #{pluralEN n "participant is" "participants are"} already registered for this course
CourseParticipantsRegistered n: Successfully registered #{n} #{pluralEN n "participant" "participants"} for course
CourseParticipantsRegisteredTutorial n: Successfully registered #{n} #{pluralEN n "participant" "participants"} for course
@ -110,9 +112,9 @@ CourseRegistrationFiles: Registration file(s)
CourseRegistrationFilesNeedReupload: Registration files need to be reuploaded every time the registration is changed
CourseRegistrationFile: Registration file
CourseRegistrationArchive: Zip archive of registration files
CourseDeregistrationNoShow: If you deregister from this course type “no show” will be recorded as your exam achievement for all exams associated with this course. If you have good reasons why you shold not be held accountable for leaving the course, please contact a course administrator. Course administrators can deregister you without incurring a permanent record.
CourseDeregistrationFromInvisibleCourse: This course type is only visible to enrolled participants and applicants. If you deregister now, you will not be able to access the course type again!
CourseDeregistrationNoReRegistration: If you deregister from the course type now, you will not be able to re-register yourself.
CourseDeregistrationNoShow: If you deregister from this course category “no show” will be recorded as your exam achievement for all exams associated with this course. If you have good reasons why you shold not be held accountable for leaving the course, please contact a course administrator. Course administrators can deregister you without incurring a permanent record.
CourseDeregistrationFromInvisibleCourse: This course category is only visible to enrolled participants and applicants. If you deregister now, you will not be able to access the course category again!
CourseDeregistrationNoReRegistration: If you deregister from the course category now, you will not be able to re-register yourself.
LoginNecessary: Please log in first!
RegisterRetry: You haven't been enrolled. Press "Enrol for course" to enrol
CourseRegisterOk: Successfully enrolled for course
@ -135,6 +137,9 @@ CourseUserTutorialsDeregistered count: Sucessfully deregistered participant from
CourseUserNoTutorialsDeregistered: Participant is not registered for any of the selected courses
CourseUserTutorials: Registered courses
CourseUserExams: Registered exams
CourseUserExamOccurrences: Exam occurrence
CourseUserExamOccurrenceOverride: Override other registrations for this exam, if any
CourseUserExamOccurrenceAgainExaminer: Possibly allow previous examiners again
CourseUserSheets: Exercise sheets
CsvColumnUserName: Participant's full name
CsvColumnUserMatriculation: Participant's AVS number
@ -148,7 +153,7 @@ CsvColumnUserExam: Exams which the user is registered for, separated by semicolo
CsvColumnUserSubmissionGroup: Registered submission group
CsvColumnUserSurname: Participant's surname
CsvColumnUserFirstName: Participant's given name
CsvColumnUserNote: Course type notes for the participant
CsvColumnUserNote: Course category notes for the participant
CourseUserCsvName tid ssh csh: #{foldCase (termToText (unTermKey tid))}-#{foldedCase (unSchoolKey ssh)}-#{foldedCase csh}-participants
CourseUserTutorial: Registered course
CourseUserExam: Registered exam
@ -175,21 +180,21 @@ AssistantsFor: Assistants
CourseAdminFor: Course administration
TutorsFor n: #{pluralENs n "Instructor" }
CorrectorsFor n: #{pluralEN n "Corrector" "Correctors"}
CourseParticipantsHeading: Course type participants
CourseParticipantsHeading: Course category participants
CourseParticipantsCount n: #{n}
CourseParticipantsCountOf n m: #{n} of #{m}
CourseVisibility: Visibility
CourseInvisible: This course type is currently only visible to course administrators, assistants, instructors, correctors, enrolled participants and applicants.
CourseInvisible: This course category is currently only visible to course administrators, assistants, instructors, correctors, enrolled participants and applicants.
CourseRegistrationInterval: Enrolment
CourseDirectRegistrationInterval: Direct enrolment
CourseDeregisterUntil time: Deregistration only until #{time}
NotRegistered: Note enrolled for this course
CourseMaterial: Material
CourseMaterialNotFree: Course type material is only accessible to members of the course, e.g. for participants, instructors, correctors or administratiors.
CourseMaterialsFoundHere: Material for this course type is available here
CourseMaterialsNoneVisible: Currently there is no material for this course type or only material to which you don't have access (e.g. because of visibility settings)
CourseSheetsFoundHere: Exercise sheets for this course type are available here
CourseSheetsNoneVisible: Currently there are no exercise sheets for this course type or only exercise sheets to which you don't have access (e.g. because of visibility settings)
CourseMaterialNotFree: Course category material is only accessible to members of the course, e.g. for participants, instructors, correctors or administratiors.
CourseMaterialsFoundHere: Material for this course category is available here
CourseMaterialsNoneVisible: Currently there is no material for this course category or only material to which you don't have access (e.g. because of visibility settings)
CourseSheetsFoundHere: Exercise sheets for this course category are available here
CourseSheetsNoneVisible: Currently there are no exercise sheets for this course category or only exercise sheets to which you don't have access (e.g. because of visibility settings)
SheetListCourse: Exercise sheets
CourseExams: Exams
CourseTutorials: Courses
@ -232,13 +237,13 @@ TutorialRegisterFrom: Register from
TutorialRegisterTo: Register to
CourseDeleteQuestion: Are you sure you want to delete the below-mentioned course?
CourseDeleted: Course type deleted
CourseDeleted: Course category deleted
UtilEditedBy name time: #{time} by #{name}
CourseDate: Date
MailSubjectLecturerInvitation tid ssh csh: [#{tid}-#{ssh}-#{csh}] Invitation to be a course administrator
LecturerInvitationAccepted lType csh: You were registered as #{lType} for #{csh}
CourseExamRegistrationTime: Registered since
CourseExamRegistrationTime: Registered on
CourseParticipantStateIsActiveFilter: View
CourseApply: Apply for course
CourseAdministrator: Course administrator

View File

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
CourseEvents: Termine
CourseEventType: Art
CourseEventType: Kategorie
CourseEventTypePlaceholder: Vorlesung, Zentralübung, ...
CourseEventTime: Zeit
CourseEventRoom: Regulärer Raum

View File

@ -8,17 +8,17 @@ CourseEventTypePlaceholder: Lecture, Exercise discussion, ...
CourseEventTime: Time
CourseEventRoom: Regular room
CourseEventRoomHidden: Room only for participants
CourseEventRoomHiddenTip: Should the room only be displayde to course type participants?
CourseEventRoomHiddenTip: Should the room only be displayde to course category participants?
CourseEventRoomIsUnset: —
CourseEventRoomIsHidden: Room is only displayed to course type associated persons (participants, instructors, correctors, etc.)
CourseEventRoomIsHidden: Room is only displayed to course category associated persons (participants, instructors, correctors, etc.)
CourseEventNote: Note
CourseEventActions: Actions
CourseEventsActionEdit: Edit
CourseEventsActionDelete: Delete
CourseEventsActionCreate: New occurrence
CourseEventCreated: Successfully created course type occurrence
CourseEventEdited: Successfully edited course type occurrence
CourseEventDeleteQuestion: Are you sure you want to delete the course type occurrence mentioned below?
CourseEventDeleted: Successfully deleted course type occurrence
CourseEventEdit: Edit course type occurrence
CourseEventNew: New course type occurrence
CourseEventCreated: Successfully created course category occurrence
CourseEventEdited: Successfully edited course category occurrence
CourseEventDeleteQuestion: Are you sure you want to delete the course category occurrence mentioned below?
CourseEventDeleted: Successfully deleted course category occurrence
CourseEventEdit: Edit course category occurrence
CourseEventNew: New course category occurrence

View File

@ -9,17 +9,17 @@ CourseNewsLastEdited time: Last changed: #{time}
CourseNewsActionEdit: Edit
CourseNewsActionDelete: Delete
CourseNewsActionCreate: Create new item
CourseNewsVisibleFromEditWarning: This item of course type news has already been published and should no longer be changed sind this might confuse participants.
CourseNewsVisibleFromEditWarning: This item of course category news has already been published and should no longer be changed sind this might confuse participants.
CourseNewsVisibleFromTip: If left empty this item is never visible. Leave empty for unfinished items
CourseNewsTitle: Title
CourseNewsSummary: Summary
CourseNewsSummaryTip: If specified this only the summary will be shown on the course type page, saving space. The content will be shown in a popup
CourseNewsSummaryTip: If specified this only the summary will be shown on the course category page, saving space. The content will be shown in a popup
CourseNewsContent: Content
CourseNewsParticipantsOnly: Only for course type participants
CourseNewsParticipantsOnly: Only for course category participants
CourseNewsVisibleFrom: Visible from
CourseNewsCreated: Successfully created item of course type news
CourseNewsEdited: Successfully edited item of course type news
CourseNewsDeleteQuestion: Are you sure you want to delete the item of course type news listed below?
CourseNewsDeleted: Successfully deleted item of course type news
CourseNewsNew: Add course type news
CourseNewsEdit: Edit item of course type news
CourseNewsCreated: Successfully created item of course category news
CourseNewsEdited: Successfully edited item of course category news
CourseNewsDeleteQuestion: Are you sure you want to delete the item of course category news listed below?
CourseNewsDeleted: Successfully deleted item of course category news
CourseNewsNew: Add course category news
CourseNewsEdit: Edit item of course category news

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Winnie Ros <winnie.ros@campus.lmu.de>
# SPDX-FileCopyrightText: 2022-25 Gregor Kleen <gregor.kleen@ifi.lmu.de>,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
@ -35,7 +35,7 @@ ExamEditHeading examn@ExamName: #{examn} bearbeiten
ExamNameTip: Muss innerhalb der Veranstaltung eindeutig sein
ExamDescription: Beschreibung
ExamFormTimes: Zeiten
ExamFormOccurrences: Prüfungstermine/Räume
ExamFormOccurrences: Prüfungstermine / Räume
ExamFormAutomaticFunctions: Automatische Funktionen
ExamFormCorrection: Korrektur
ExamFormParts: Teile
@ -43,12 +43,13 @@ ExamFormMode: Ausgestaltung der Prüfung
ExamFormGrades: Prüfungsleistungen
ExamStart: Beginn
ExamEnd: Ende
ExamTimeTip: Nur zur Information der Studierenden, die tatsächliche Zeitangabe erfolgt pro Prüfungstermin/Raum
ExamTimeTip: Nur zur Information, die tatsächliche Zeitangabe erfolgt pro Prüfungstermin/Raum.
ExamTimeFilterTip: In der Kursansicht für Kursverwaltende wird diese Zeit zur Filterung verwendet.
ExamVisibleFrom: Sichtbar ab
ExamVisibleFromTip: Ohne Datum nie sichtbar und keine Anmeldung möglich
ExamRegisterFrom: Anmeldung ab
ExamRegisterTo: Anmeldung bis
ExamRegisterFromTip: Zeitpunkt ab dem sich Kursartteilnehmer:innen selbständig zur Prüfung anmelden können; ohne Datum ist keine Anmeldung möglich
ExamRegisterFromTip: Zeitpunkt ab dem sich Kursartteilnehmer:innen selbständig zur Prüfung anmelden können; ohne Datum ist keine selbständige Anmeldung möglich
ExamDeregisterUntil: Abmeldung bis
ExamPublishOccurrenceAssignments: Termin- bzw. Raumzuteilung den Teilnehmer:innen mitteilen um
ExamPublishOccurrenceAssignmentsTip: Ab diesem Zeitpunkt können Teilnehmer:innen einsehen zu welcher Teilprüfung bzw. welchen Raum sie angemeldet sind
@ -64,14 +65,15 @@ ExamAutomaticGradingTip: Sollen die Gesamtleistungen der Teilnehmer:innen automa
ExamBonus: Bonuspunkte-System
ExamGradingMode: Bewertungsmodus
ExamGradingModeTip: In welcher Form werden Prüfungsleistungen für diese Prüfung eingetragen?
ExamStaff: Prüfer:innen/Verantwortliche Hochschullehrer:innen
ExamStaffTip: Geben Sie bitte in jedem Fall einen Namen an, der Prüfer:in/Veranstalter:in/Hochschullehrer:in eindeutig identifiziert! Sollte der Name des Prüfers/der Prüferin allein womöglich nicht eindeutig sein, so geben Sie bitte eindeutig identifizierende Zusatzinfos, wie beispielsweise den Lehrstuhl bzw. die LFE o.Ä., an.
ExamStaff: Hauptverantworliche:r
ExamStaffTip: Hauptverantwortliche:r Prüfer:in, Textfeld zur reinen Information der Teilnehmenden.
ExamExamOfficeSchools: Zusätzliche Bereiche
ExamExamOfficeSchoolsTip: Prüfungsbeauftragte von Bereichen, die Sie hier angeben, erhalten im System (zusätzlich zum primären Bereich der zugehörigen Kursart) volle Einsicht in sämtliche für diese Prüfung hinterlegten Leistungen, unabhängig von den Studiendaten der Teilnehmer:innen.
ExamCorrectorEmail: E-Mail
ExamCorrectors: Korrektor:innen
ExamCorrectorsTip: Hier eingetragene Korrektor:innen können zwischen Beginn der Prüfung und "Bewertung abgeschlossen ab" Ergebnisse für alle Teilprüfungen und alle Teilnehmer:innen im System hinterlegen.
ExamCorrectorAlreadyAdded: Ein Korrektor/eine Korrektorin mit dieser E-Mail ist bereits für diese Prüfung eingetragen
ExamCorrectors: Prüfer:innen
ExamCorrectorsTip: Hier eingetragene Prüfer:innen können zwischen Beginn der Prüfung und "Bewertung abgeschlossen ab" Ergebnisse für alle Teilprüfungen und alle Teilnehmer:innen im System hinterlegen.
ExamCorrectorAlreadyAdded: Ein Prüfer:innen mit dieser E-Mail ist bereits für diese Prüfung eingetragen
ExamParticipant: Prüfungsteilnehmer:in
ExamRoom: Raum
ExamRoomManual': Keine automatische bzw. selbstständige Zuteilung
ExamRoomSurname': Nach Nachname
@ -86,6 +88,7 @@ ExamRoomAlreadyExists: Prüfung ist bereits eingetragen
ExamRoomName: Interne Bezeichnung
ExamRoomCapacity: Kapazität
ExamRoomCapacityNegative: Kapazität darf nicht negativ sein
ExamRoomCapacityInsufficient n@Int: Kapazität reicht nicht aus, #{noneOneMoreDE n "keine Plätze" "nur noch ein Platz" ("nur noch " <> tshow n <> " Plätze")} verfügbar
ExamRoomTime: Termin
ExamRoomStart: Beginn
ExamRoomEnd: Ende
@ -123,6 +126,8 @@ ExamOccurrenceStartMustBeAfterExamStart eoName@ExamOccurrenceName: Beginn des Te
ExamOccurrenceEndMustBeBeforeExamEnd eoName@ExamOccurrenceName: Ende des Termins #{eoName} liegt nach dem Ende der Prüfung
ExamOccurrenceDuplicate eoRoom@Text eoRange@Text: Raum #{eoRoom}, Termin #{eoRange} kommt mehrfach mit der selben Beschreibung vor
ExamOccurrenceDuplicateName eoName@ExamOccurrenceName: Interne Terminbezeichnung #{eoName} kommt mehrfach vor
ExamOccurrenceExaminerIsUnset !ident-ok: —
ExamOccurrenceExaminerIsHidden: Prüfer wird nur Teilnehmer:innen angezeigt
ExamOccurrenceRoomIsUnset !ident-ok: —
ExamOccurrenceRoomIsHidden: Raum wird nur Teilnehmer:innen angezeigt
ExamOccurrenceCannotBeDeletedDueToRegistrations eoName@ExamOccurrenceName: Termin #{eoName} kann nicht gelöscht werden, da noch Teilnehmer:innen diesem Termin zugewiesen sind. Über die Liste von Prüfungsteilnehmern können Sie zunächst die entsprechenden Terminzuweisungen entfernen.
@ -217,6 +222,13 @@ ExamOccurrenceRuleParticipant: Termin- bzw. Raumzuteilungsverfahren
ExamRegisteredCount: Anmeldungen
ExamRegisteredCountOf num@Int64 count@Int64 !ident-ok: #{num}/#{count}
ExamOccurrences: Termine
ExamOccurrencesCopied num@Int: #{pluralDEeN num "Prüfungstermin"} kopiert
ExamOccurrencesEdited num@Int del@Int: #{pluralENsN num "Prüfungstermin"} geändert #{guardMonoid (del > 0) ("und " <> pluralENsN num "Prüfungstermin" <> " gelöscht")}
ExamOccurrenceCopyNoStartDate: Dieser Kurs hat noch keine eigene Termine um Prüfungstermine zeitlich damit zu assoziieren
ExamOccurrenceCopyFail: Keine passenden Prüfungstermine zum Kopieren gefunden
ExaminerReocurrence examiner@Text: Mehrfache Prüfung durch #{examiner}!
ExamProblemReoccurrence: Prüfungen mit wiederholt gleichem Prüfer
ExamNoProblemReoccurrence: Heute keine Prüfungen mit wiederholtem Prüfer.
GradingFrom: Ab
ExamNoShow: Nicht erschienen
ExamVoided: Entwertet
@ -264,6 +276,7 @@ ExamAutoOccurrenceExceptionRoomTooSmall: Automatische Verteilung gescheitert. Ei
ExamBonusInfoPoints: Zur Berechnung von Bonuspunkten werden nur jene Blätter herangezogen, deren Aktivitätszeitraum vor Start des jeweiligen Termin/Prüfung begonnen hat
ExamUserCsvSheetName tid@TermId ssh@SchoolId csh@CourseShorthand examn@ExamName: #{foldCase (termToText (unTermKey tid))}-#{foldedCase (unSchoolKey ssh)}-#{foldedCase csh}-#{foldedCase examn} Teilnehmer
ExamRoomExaminerTip: Nur bereits eingetragene Prüfer:innen sind hier erlaubt
ExamRoomCapacityTip: Maximale Anzahl an Prüfungsteilnehmern für diesen Termin/Raum; leer lassen für unbeschränkte Teilnehmeranzahl
ExamRoomMappingRandom: Verteilung
ExamFinishHeading: Prüfungsergebnisse sichtbar schalten

View File

@ -1,11 +1,11 @@
# SPDX-FileCopyrightText: 2022 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Winnie Ros <winnie.ros@campus.lmu.de>
# SPDX-FileCopyrightText: 2022-25 Gregor Kleen <gregor.kleen@ifi.lmu.de>,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
ExamRegistrationInviteDeadline: Invitation valid until
ExamRegistrationEnlistDirectly: Register known users directly
ExamRegistrationEnlistDirectlyTip: Should users whose email addresses are known to the system be registered for the exam directly? Otherwise invitations will be sent to alle users, which they will have to accept first in order to be registered. Unknown users always receive an invitation.
ExamRegistrationRegisterCourse: Also enroll users in course type
ExamRegistrationRegisterCourse: Also enroll users in course category
ExamRegistrationRegisterCourseTip: Users that aren't enrolled already won't be registered for the exam otherwise.
ExamRegistrationInviteField: Email addresses
ExamParticipantsRegisterHeading: Add exam participants
@ -35,7 +35,7 @@ ExamEditHeading examn: Edit #{examn}
ExamNameTip: Needs to be unique within the course
ExamDescription: Description
ExamFormTimes: Times
ExamFormOccurrences: Occurrences/rooms
ExamFormOccurrences: Occurrences / Rooms
ExamFormAutomaticFunctions: Automatic functions
ExamFormCorrection: Correction
ExamFormParts: Exam parts
@ -43,12 +43,13 @@ ExamFormMode: Exam design
ExamFormGrades: Exam achievements
ExamStart: Start
ExamEnd: End
ExamTimeTip: Only for informational purposes. The actual times are set for each occurrence/room
ExamTimeTip: Only for informational purposes. The actual times are set for each occurrence/room.
ExamTimeFilterTip: Also used for Filtering in the course administrator course view.
ExamVisibleFrom: Visible from
ExamVisibleFromTip: If left empty the exam is never visible and course type participants may not register.
ExamVisibleFromTip: If left empty the exam is never visible and course category participants may not register.
ExamRegisterFrom: Register from
ExamRegisterTo: Register to
ExamRegisterFromTip: Start of the period in which course type participants may register themselves for the exam. If left empty participants are never allowed to register.
ExamRegisterFromTip: Start of the period in which course category participants may register themselves for the exam. If left empty participants are never allowed to register themselves.
ExamDeregisterUntil: Deregister until
ExamPublishOccurrenceAssignments: Publish occurrence/room-assignments
ExamPublishOccurrenceAssignmentsTip: At this time participants can find out to which occurrence/room they are assigned
@ -64,14 +65,15 @@ ExamAutomaticGradingTip: Should the exam achievement be automatically computed f
ExamBonus: Bonus point system
ExamGradingMode: Grading mode
ExamGradingModeTip: In which format should grades for this exam be entered?
ExamStaff: Examiner/Responsible university teacher
ExamStaffTip: Please always specify a name that uniquely identifies the examiner/organiser/repsonsible university teacher! If there is a possibility that the name alone is ambiguous please also specify some additional information e.g. the professorial chair or the educational and research unit.
ExamStaff: Chief examiner
ExamStaffTip: Primary responsible examiner, arbirary text field for pure informational purposes.
ExamExamOfficeSchools: Additional departments
ExamExamOfficeSchoolsTip: Exam offices of departments you specify here will also have full access to all results for this exam disregarding the individual participants' features of study.
ExamCorrectorEmail: Email
ExamCorrectors: Correctors
ExamCorrectorsTip: Correctors configured here may, after the start of the exam and until "Results visible from", enter exam part results for all exam parts and participants.
ExamCorrectorAlreadyAdded: A corrector with this email address already exists
ExamCorrectors: Examiner
ExamCorrectorsTip: Examiners configured here may, after the start of the exam and until "Results visible from", enter exam part results for all exam parts and participants.
ExamCorrectorAlreadyAdded: An examiner with this email address already exists
ExamParticipant: Examinee
ExamRoom: Room
ExamRoomManual': No automatic or autonomous assignment
ExamRoomSurname': By surname
@ -86,6 +88,7 @@ ExamRoomAlreadyExists: Occurrence already configured
ExamRoomName: Internal name
ExamRoomCapacity: Capacity
ExamRoomCapacityNegative: Capacity may not be negative
ExamRoomCapacityInsufficient n@Int: Insufficient capacity, #{noneOneMoreEN n "none" "just one" ("only " <> tshow n)} remaining
ExamRoomTime: Time
ExamRoomStart: Start
ExamRoomEnd: End
@ -123,8 +126,10 @@ ExamOccurrenceStartMustBeAfterExamStart eoName: Start of the occurrence #{eoName
ExamOccurrenceEndMustBeBeforeExamEnd eoName: End of the occurrence #{eoName} must be before the exam end
ExamOccurrenceDuplicate eoRoom eoRange: Combination of room #{eoRoom} and occurrence #{eoRange} occurs multiple times
ExamOccurrenceDuplicateName eoName: Internal name #{eoName} occurs multiple times
ExamOccurrenceExaminerIsUnset !ident-ok: —
ExamOccurrenceExaminerIsHidden: Examiner only displayed to participants registered for this occurrence
ExamOccurrenceRoomIsUnset: —
ExamOccurrenceRoomIsHidden: Room is only displayed to participants registered for this occurrence/room
ExamOccurrenceRoomIsHidden: Room only displayed to participants registered for this occurrence
ExamOccurrenceCannotBeDeletedDueToRegistrations eoName: Occurrence #{eoName} cannot be deleted because participants are registered for it. You can remove the offending registrations via the list of exam participants.
ExamRegistrationMustFollowSchoolSeparationFromStart dayCount: As per school rules there #{pluralEN dayCount "needs" "need"} to be at least #{dayCount} #{pluralEN dayCount "day" "days"} between "Register from" and "Start".
ExamRegistrationMustFollowSchoolDuration dayCount: As per school rules there #{pluralEN dayCount "needs" "need"} to be at least #{dayCount} #{pluralEN dayCount "day" "days"} between "Register from" and "Register to".
@ -159,7 +164,7 @@ CsvColumnExamUserExercisePassesMax: Maximum number of exercise sheets the partic
CsvColumnExamUserBonus: Exam bonus points
CsvColumnExamUserParts: Number of points the participant achieved per exam part. One column per exam part if applicable.
CsvColumnExamUserResult: Exam achievement; "passed", "failed", "no-show", "voided", or any number grade ("1.0", "1.3", "1.7", ..., "4.0", "5.0")
CsvColumnExamUserCourseNote: Course type notes for the participant
CsvColumnExamUserCourseNote: Course category notes for the participant
CsvColumnExamOfficeExamUserOccurrenceStart: Exam occurrence (ISO 8601)
CsvColumnUserStudyFeatures: All relevant features of study for the participant, separated by semicolon (;)
ExamUserCsvName tid ssh csh examn: #{foldCase (termToText (unTermKey tid))}-#{foldedCase (unSchoolKey ssh)}-#{foldedCase csh}-#{foldedCase examn}-participants
@ -176,7 +181,7 @@ ExamAction: Action
ExamUsersExamDataRequiresRegistration: If exam data (part-/result, occurrence/room, bonus) is to be modified/set, the relenvant participant needs to be registered for the exam.
ExamNoOccurrence: No occurrence/room
ExamBonusNone: No bonus points
ExamUserCsvCourseNoteDeleted: Course type note will be deleted
ExamUserCsvCourseNoteDeleted: Course category note will be deleted
ExamUsersDeregistered count: Successfully deregistered #{show count} #{pluralEN count "participant" "participants"}
ExamUsersOccurrenceUpdated count: Successfully assigned occurrence/room for #{show count} #{pluralEN count "participant" "participants"}
ExamUsersResultsAccepted count: Successfully accepted computed result for #{show count} #{pluralEN count "participant" "participants"}
@ -217,6 +222,13 @@ ExamOccurrenceRuleParticipant: Occurrence/room assignment procedure
ExamRegisteredCount: Registrations
ExamRegisteredCountOf num count: #{num}/#{count}
ExamOccurrences: Exams
ExamOccurrencesCopied num: #{pluralENsN num "exam occurrence"} copied
ExamOccurrencesEdited num del: #{pluralENsN num "exam occurrence"} edited #{guardMonoid (del > 0) ("and " <> pluralENsN num "exam occurrence" <> " deleted")}
ExamOccurrenceCopyNoStartDate: This course needs its own occurrence to copy associated exam occurrences.
ExamOccurrenceCopyFail: No suitable exam occurrences found to copy from.
ExaminerReocurrence examiner: Multiple examinations by #{examiner}!
ExamProblemReoccurrence: Exams with reoccurring examiner
ExamNoProblemReoccurrence: Today there are no exams with a reoccurring examiner.
GradingFrom: From
#templates widgets/bonus-rule
@ -262,6 +274,8 @@ ExamAutoOccurrenceExceptionNoUsers: No participants can be distributed with the
ExamAutoOccurrenceExceptionRoomTooSmall: Automatic distribution failed. A different distribution procedure might succeed. Alternatively, minimizing rooms or removing small rooms might help.
ExamBonusInfoPoints: When calculating an exam bonus only those sheets will be considered, for which the submission period started before the start of the relevant occurrence/room
ExamUserCsvSheetName tid ssh csh examn: #{foldCase (termToText (unTermKey tid))}-#{foldedCase (unSchoolKey ssh)}-#{foldedCase csh}-#{foldedCase examn} Participants
ExamRoomExaminerTip: Only examiners allowed here, add beforehand
ExamRoomCapacityTip: Maximum number of participants for this occurrence/room; leave empty for unlimited capacity
ExamRoomMappingRandom: Distribution
ExamFinishHeading: Make results visible
@ -302,9 +316,9 @@ ExamUserCsvOverrideResult: Override exam result in contradiction of computed val
ExamUserCsvSetBonus: Set bonus points
ExamUserCsvSetResult: Set exam result
ExamUserCsvSetPartResult: Set result for exam part
ExamUserCsvSetCourseNote: Modify course type participant notes
ExamUserCsvExceptionNoMatchingUser: Course type participant could not be identified uniquely. All identifiers (given name(s), surname, display name, matriculation, ..) must match exactly. You can try to remove some of the identifiers for the given line (i.e. all but matriculation). Uni2work will then search for users using only the remaining identifiers. In this case special care should be taken that Uni2work correctly identifies the intended user.
ExamUserCsvExceptionMultipleMatchingUsers: Course type participant could not be identified uniquely. There are multiple users that match the given identifiers. You can try to add more identifiers for the given line to ensure that only the intended user can be identified with them.
ExamUserCsvSetCourseNote: Modify course category participant notes
ExamUserCsvExceptionNoMatchingUser: Course category participant could not be identified uniquely. All identifiers (given name(s), surname, display name, matriculation, ..) must match exactly. You can try to remove some of the identifiers for the given line (i.e. all but matriculation). Uni2work will then search for users using only the remaining identifiers. In this case special care should be taken that Uni2work correctly identifies the intended user.
ExamUserCsvExceptionMultipleMatchingUsers: Course category participant could not be identified uniquely. There are multiple users that match the given identifiers. You can try to add more identifiers for the given line to ensure that only the intended user can be identified with them.
ExamUserCsvExceptionNoMatchingStudyFeatures: The specified field did not match with any of the participant's fields of study. You can try to remove the field of study for the given line. Uni2work will then automatically choose a field of study.
ExamUserCsvExceptionNoMatchingOccurrence: Occurrence/room could not be identified uniquely. Please ensure that the given line only contains internal room identifiers exactly as they have been configured for this exam.
ExamUserCsvExceptionMismatchedGradingMode expectedGradingMode actualGradingMode: The imported data contained an exam achievement which does not match the grading mode for this exam. The expected grading mode can be changed at "Edit exam" ("Passed/Failed", "Numeric grades", or "Mixed").

View File

@ -15,8 +15,8 @@ ExternalExamCorrectErrorNeedleTooShort: This identifier is too short.
UnauthorizedExternalExamCorrectorGrade: You may not enter overall exam achievements for this exam.
ExternalExamCorrectErrorMultipleMatchingUsers: This identifier matches on multiple students.
ExternalExamCorrectErrorNoMatchingUsers: This identifier does not match any student.
ExternalExamEdited coursen@CourseName examn@ExamName: Succesfully edited exam “#{examn}” for course type “#{coursen}”.
ExternalExamExists coursen@CourseName examn@ExamName: Exam “#{examn}” already exists for course type “#{coursen}”.
ExternalExamEdited coursen@CourseName examn@ExamName: Succesfully edited exam “#{examn}” for course category “#{coursen}”.
ExternalExamExists coursen@CourseName examn@ExamName: Exam “#{examn}” already exists for course category “#{coursen}”.
ExternalExamEdit coursen examn: Edit: #{coursen}, #{examn}
ExternalExamSemester: Year
ExternalExamSchool: Department
@ -41,10 +41,10 @@ ExternalExamStaffTip: The list of ssociated persons is shown to exam offices and
ExternalExamStaffAlreadyAdded: Person is already associated with the exam.
ExternalExamStaffEmail: Email
ExternalExamUserMustBeStaff: You yourself must always be an associated person for exams you create.
ExternalExamCourseExists: This course type already exists with FRADrive. Exams for courses that exist within FRADrive should be associated with the course type directly instead of being created as an external exam.
ExternalExamCourseExists: This course category already exists with FRADrive. Exams for courses that exist within FRADrive should be associated with the course category directly instead of being created as an external exam.
HeadingExternalExamList: External exams
HeadingExternalExamNew: New external exam
ExternalExamCreated coursen@CourseName examn@ExamName: Succesfully created exam “#{examn}” for course type “#{coursen}”.
ExternalExamCreated coursen@CourseName examn@ExamName: Succesfully created exam “#{examn}” for course category “#{coursen}”.
MailSubjectExternalExamStaffInvitation coursen examn: Invitation to act as examiner for “#{examn}” of “#{coursen}”
ExternalExamOccurrenceEdited count: Successfully edited #{count} #{pluralEN count "occurrence" "occurrences"}
ExternalExamResultEdited count: Successfully edited #{count} #{pluralEN count "exam result" "exam results"}

View File

@ -4,7 +4,7 @@
MaterialList !ident-ok: Material
MaterialName !ident-ok: Name
MaterialType: Art
MaterialType: Typ
MaterialTypePlaceholder: Folien, Code, Beispiel, ...
MaterialTypeSlides: Folien
MaterialTypeCode !ident-ok: Code

View File

@ -11,27 +11,27 @@ MaterialTypeCode: Code
MaterialTypeExample: Example
MaterialDescription: Description
MaterialVisibleFrom: Visible to participants from
MaterialVisibleFromTip: Never visible to participants if left empty; leaving the date empty is only sensible for unfinished course type material or when course type material should be provided only to sheet correctors
MaterialVisibleFromEditWarning: This course type material has already been published and should not be edited. Doing so might confuse the participants.
MaterialInvisible: This course type material is currently invisible to participants!
MaterialVisibleFromTip: Never visible to participants if left empty; leaving the date empty is only sensible for unfinished course category material or when course category material should be provided only to sheet correctors
MaterialVisibleFromEditWarning: This course category material has already been published and should not be edited. Doing so might confuse the participants.
MaterialInvisible: This course category material is currently invisible to participants!
MaterialFiles: Files
MaterialHeading materialName: #{materialName}
MaterialListHeading: Course type materials
MaterialNewHeading: Publish new course type material
MaterialNewTitle: New course type material
MaterialEditHeading materialName: Edit course type material “#{materialName}”
MaterialEditTitle materialName: Edit course type material “#{materialName}”
MaterialSaveOk tid ssh csh materialName: Successfully saved “#{materialName}” for course type #{tid}-#{ssh}-#{csh}
MaterialNameDup tid ssh csh materialName: Course type material with the name “#{materialName}” already exists for course type #{tid}-#{ssh}-#{csh}
MaterialDeleteCaption: Do you really want to delete the course type material mentioned below?
MaterialListHeading: Course category materials
MaterialNewHeading: Publish new course category material
MaterialNewTitle: New course category material
MaterialEditHeading materialName: Edit course category material “#{materialName}”
MaterialEditTitle materialName: Edit course category material “#{materialName}”
MaterialSaveOk tid ssh csh materialName: Successfully saved “#{materialName}” for course category #{tid}-#{ssh}-#{csh}
MaterialNameDup tid ssh csh materialName: Course category material with the name “#{materialName}” already exists for course category #{tid}-#{ssh}-#{csh}
MaterialDeleteCaption: Do you really want to delete the course category material mentioned below?
MaterialDelHasFiles count: including #{count} #{pluralEN count "file" "files"}
MaterialIsVisible: Caution, this course type material has already been published.
MaterialDeleted materialName: Successfully deleted course type material “#{materialName}”
MaterialIsVisible: Caution, this course category material has already been published.
MaterialDeleted materialName: Successfully deleted course category material “#{materialName}”
MaterialArchiveName tid ssh csh materialName: #{foldCase (termToText (unTermKey tid))}-#{foldedCase (unSchoolKey ssh)}-#{foldedCase csh}-#{foldedCase materialName}
MaterialVideo materialName: #{materialName} - Video
MaterialVideoUnsupported: Your browser does not seem to support embedded video
MaterialVideoDownload: Download
MaterialFree: Course type material is publicly available.
MaterialFree: Course category material is publicly available.
AccessibleSince: Accessible since
VisibleFrom: Published
FilterMaterialNameSearch !ident-ok: Name

View File

@ -2,16 +2,16 @@
#
# SPDX-License-Identifier: AGPL-3.0-or-later
ParticipantsList: Lists of course type participants
ParticipantsIntersect: Common course type participants
ParticipantsList: Lists of course category participants
ParticipantsIntersect: Common course category participants
ParticipantsCsvName tid ssh: #{foldCase (termToText (unTermKey tid))}-#{foldedCase (unSchoolKey ssh)}-participants
ParticipantsIntersectCourseOption tid@TermId ssh@SchoolId coursen@CourseName: #{tid} - #{ssh} - #{coursen}
ParticipantsIntersectCourses: Courses
CourseParticipantsRegisteredWithoutField n: #{n} #{pluralEN n "participant was" "participants were"} registered without #{pluralEN n "an associated field of study" "associated fields of study"}, because #{pluralEN n "it" "they"} could not be determined uniquely.
ParticipantsCsvSheetName tid ssh: #{foldCase (termToText (unTermKey tid))}-#{foldedCase (unSchoolKey ssh)} Participants
CourseParticipants n: Currently #{n} course type #{pluralEN n "participant" "participants"}
CourseParticipants n: Currently #{n} course category #{pluralEN n "participant" "participants"}
ParticipantsIntersectNotOne: Intersection
AllUsersUnion: Union of all participants
AllUsersIntersection: Intersection of all participants
CourseDeleteActiveParticipants: This course type still has active participants. Remove all active participants first if you really want to delete this course.
CourseDeleteExistExams: This course type cannot be deleted, for as long as associated exams exist.
CourseDeleteActiveParticipants: This course category still has active participants. Remove all active participants first if you really want to delete this course.
CourseDeleteExistExams: This course category cannot be deleted, for as long as associated exams exist.

View File

@ -13,8 +13,8 @@ SheetDeleteQuestion: Do you really want to delete the below-mentioned exercise s
SheetDeleted: Successfully deleted exercise sheet
SheetArchiveName tid ssh csh shn: #{foldCase (termToText (unTermKey tid))}-#{foldedCase (unSchoolKey ssh)}-#{foldedCase csh}-#{foldedCase shn}
SheetTypeArchiveName tid ssh csh shn renderedSft: #{foldCase (termToText (unTermKey tid))}-#{foldedCase (unSchoolKey ssh)}-#{foldedCase csh}-#{foldedCase shn}-#{foldCase renderedSft}
SheetEditOk tid ssh csh sheetName: Successfully saved exercise sheet #{sheetName} in course type #{tid}-#{ssh}-#{csh}
SheetNameDup tid ssh csh sheetName: There already is an exercise sheet #{sheetName} in course type #{tid}-#{ssh}-#{csh}
SheetEditOk tid ssh csh sheetName: Successfully saved exercise sheet #{sheetName} in course category #{tid}-#{ssh}-#{csh}
SheetNameDup tid ssh csh sheetName: There already is an exercise sheet #{sheetName} in course category #{tid}-#{ssh}-#{csh}
SheetVisibleFrom: Visible from (for participants)
SheetVisibleFromTip: Always invisible for participants and no submission possible if left empty; only leave this field empty for temporary/unfinished sheets
SheetActiveFrom: Active from/Submission period start
@ -24,7 +24,7 @@ SheetSolutionFromTip: Always invisible for participants if left empty; corrector
SheetName: Name
SheetDescription: Description
SheetRequireExam: Require registration for an exam?
SheetRequireExamTip: If registration for an exam is required, only course type participants that are registered for that exam at the time of submission will be allowed to create submission. Download of sheet files will also be restricted to course type participants registered for the exam.
SheetRequireExamTip: If registration for an exam is required, only course category participants that are registered for that exam at the time of submission will be allowed to create submission. Download of sheet files will also be restricted to course category participants registered for the exam.
SheetRequiredExam: Exam
SheetFormType: Valuation & submission
SheetFormTimes: Times
@ -37,15 +37,15 @@ SheetMarkingFiles: Correction
SheetMarkingTip: Instructions for correction, visible only to correctors
SheetPersonalisedFilesDownload: Download personalised sheet files
SheetPersonalisedFiles: Personalised sheet files
SheetPersonalisedFilesTip: Should course type participants be assigned personalised sheet files in addition to the files configured above? Only the user to which a file has been assigned may view it.
SheetPersonalisedFilesTip: Should course category participants be assigned personalised sheet files in addition to the files configured above? Only the user to which a file has been assigned may view it.
SheetPersonalisedFilesUpload: Personalised sheet files
SheetPersonalisedFilesUploadTip: Download the template for a ZIP-archive of personalised sheet files, move files into the directories corresponding to the desired users and upload the archive again. If the name of a personalised file matches the name of an unpersonalised file, the personalised file replaces the unpersonalised one from the respective participants' point of view.
SheetPersonalisedFilesKeepExisting: Keep existing files
SheetPersonalisedFilesKeepExistingTip: Should the personalised files you upload be added to the already existing ones, if applicable? Otherwise the files you upload will completely replace any existing files.
SheetPersonalisedFilesAllowNonPersonalisedSubmission: Allow non-personalised submission
SheetPersonalisedFilesAllowNonPersonalisedSubmissionTip: Should course type participants with no assigned personalised files be allowed to submit anyway?
SheetPersonalisedFilesAllowNonPersonalisedSubmissionTip: Should course category participants with no assigned personalised files be allowed to submit anyway?
SheetPersonalisedFilesDownloadTemplateHere: You can download a template for a ZIP-archive of personalised sheet files with the structure that Uni2work expects here:
SheetPersonalisedFilesUsersList: List of course type participants who have personalised sheet files
SheetPersonalisedFilesUsersList: List of course category participants who have personalised sheet files
SheetPersonalisedFilesMetaYAMLSeedComment: This string was generated cryptographically from data uniquely identifying the user and exercise sheet. You can use it as a seed for a pseudorandom generator for generating (parts of) the personalised files.
SheetPersonalisedFilesMetaYAMLNoSeedComment: There is not enough information available to generate a seed. You will have to create the exercise sheet in Uni2work first. Once seeds can be generated they will be generated cryptographically and you may use them to generate (parts of) the personalised files.
SheetActiveFromTip: The exercise sheet's assignment will only be available for download and submission starting at this time. If left empty no submission or download of assignment is ever allowed
@ -63,7 +63,7 @@ SheetErrDeadlineEarly: "Submission period end" must be after "Submission period
SheetErrHintEarly: "Hint from" must be after "Submission period start"
SheetErrSolutionEarly: "Solution from" must be after "Submission period end"
SheetErrVisibleWithoutActive: If “Visible from (for participants)” is specified “Active from/Submission period start” must also be specified
SheetSubmissionModeNoneWithoutNotGraded: The sheet was configured to be "No submission" but not "Not marked". Course type participants will not be able to submit.
SheetSubmissionModeNoneWithoutNotGraded: The sheet was configured to be "No submission" but not "Not marked". Course category participants will not be able to submit.
SheetWarnNoActiveTo: “Active to/Submission period end” should always be specified
CountTutProp: Courses count against proportion
CountTutPropTip: If submissions are assigned by course, do those assignments count with regard to the set proportion?
@ -82,13 +82,13 @@ RatingPercent: Achieved
IsRated: Marked
SheetTypeIsExam: Rating „as an exam part“
SheetGradingSummaryTitle intgr: #{intgr} #{pluralEN intgr "sheet" "sheets"}
PersonalisedSheetFilesIgnored count: #{count} uploaded #{pluralEN count "file was" "files were"} ignored because #{pluralEN count "it" "they"} could not be associated with both a sheet file type and a course type participant.
PersonalisedSheetFilesIgnored count: #{count} uploaded #{pluralEN count "file was" "files were"} ignored because #{pluralEN count "it" "they"} could not be associated with both a sheet file type and a course category participant.
PersonalisedSheetFilesIgnoredIntro: The following files were ignored:
PersonalisedSheetFilesDownloadRestrictByExamNone: No restriction
PersonalisedSheetFilesDownloadRestrictByExam: Restrict to exam participants
PersonalisedSheetFilesDownloadRestrictByExamTip: Only download personalised sheet files for participants also registered to a certain exam?
PersonalisedSheetFilesDownloadAnonymousField: Anonymisation
PersonalisedSheetFilesDownloadAnonymousFieldTip: Should the ZIP-archive of personalised files be anonymised (it would then contain no immediately identifiable information regard the course type participants) or should directory names be decorated with an identifiable feature of the user and the files of meta information contain additional personal data?
PersonalisedSheetFilesDownloadAnonymousFieldTip: Should the ZIP-archive of personalised files be anonymised (it would then contain no immediately identifiable information regard the course category participants) or should directory names be decorated with an identifiable feature of the user and the files of meta information contain additional personal data?
PersonalisedSheetFilesMetaFilename uid: meta-information_#{toPathPiece uid}.yaml
PersonalisedSheetFilesArchiveName tid ssh csh shn: #{foldCase (termToText (unTermKey tid))}-#{foldedCase (unSchoolKey ssh)}-#{foldedCase csh}-#{foldedCase shn}-personalised_files
SheetGeneratePseudonym: Generate

View File

@ -49,10 +49,10 @@ SubmissionArchive: Zip-archive of submission files
SubmissionArchiveCorrected: Zip-archive of submission files including corrections
SubmissionFile: Submission file
SubmissionFiles: Submitted files
EmailInvitationWarningPrevCoSubmittors: This address could not be matched to any course type participant with whom you have submitted for this course type before. An Invitation will be sent via email.
EmailInvitationWarningCourseParticipants: This address coulde not be matched to any course type participant. An Invitation will be sent via email.
MultiUserFieldExplanationPrevCoSubmittors: This input searches through the addresses of all course type participants for whom it could be determined, that you have already submitted with that person for this course.
MultiUserFieldExplanationCourseParticipants: This input searches through the addresses of all course type participants.
EmailInvitationWarningPrevCoSubmittors: This address could not be matched to any course category participant with whom you have submitted for this course category before. An Invitation will be sent via email.
EmailInvitationWarningCourseParticipants: This address coulde not be matched to any course category participant. An Invitation will be sent via email.
MultiUserFieldExplanationPrevCoSubmittors: This input searches through the addresses of all course category participants for whom it could be determined, that you have already submitted with that person for this course.
MultiUserFieldExplanationCourseParticipants: This input searches through the addresses of all course category participants.
SubmissionAlreadyExistsFor email: #{email} already has a submission for this sheet.
SubmissionUsersEmpty: Submissions may not be created without submittors.
SubmissionUserAlreadyAdded: This user is already configured as a submittor
@ -155,7 +155,7 @@ SubmissionSomeUsersDuplicateWarning: Some submittors are also submittors for a d
EMailUnknown email: Email #{email} does not belong to any known user.
CorDeficitProportion: Deficit (proportion)
CosubmittorTip: Invitations are sent via email to exactly those addresses for which it cannot be determined, that you have already submitted for this course type with the associated person, at least once. If one of the specified addresses can be matched to a person with whom you have submitted at least once for this course type already, the name of that person will be shown and the submission will immediately be made in their name as well.
CosubmittorTip: Invitations are sent via email to exactly those addresses for which it cannot be determined, that you have already submitted for this course category with the associated person, at least once. If one of the specified addresses can be matched to a person with whom you have submitted at least once for this course category already, the name of that person will be shown and the submission will immediately be made in their name as well.
CorrDownload: Download
SubmissionDownloadAnonymous: Anonymized
SubmissionDownloadSurnames: With surnames
@ -237,9 +237,9 @@ SubmissionFilterAuthorshipStatementCurrent: Current wording
SubmissionNoUsers: This submission has no associated users!
CsvColumnCorrectionTerm: Term of the course type of the submission
CsvColumnCorrectionSchool: School of the course type of the submission
CsvColumnCorrectionCourse: Shorthand of the course type of the submission
CsvColumnCorrectionTerm: Term of the course category of the submission
CsvColumnCorrectionSchool: School of the course category of the submission
CsvColumnCorrectionCourse: Shorthand of the course category of the submission
CsvColumnCorrectionSheet: Name of the sheet of the submission
CsvColumnCorrectionSubmission: Number of the submission (uwa…)
CsvColumnCorrectionSurname: Submittor's surnames, separated by semicolon (;)

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 Winnie Ros <winnie.ros@campus.lmu.de>
# SPDX-FileCopyrightText: 2022-25 Winnie Ros <winnie.ros@campus.lmu.de>,Steffen Jost <s.jost@fraport.de>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
@ -36,6 +36,7 @@ TutorialDelete: Löschen
TutorialsHeading: Kurse
TutorialNew: Neuer Kurs
TutorialRegisteredSuccess tutn@TutorialName: Erfolgreich zum Kurs #{tutn} angemeldet
TutorialRegisteredFail tutn@TutorialName: Anmeldung zum Kurs #{tutn} fehlgeschlagen. Existiert bereits eine Anmeldung?
TutorialDeregisteredSuccess tutn@TutorialName: Erfolgreich vom Kurs #{tutn} abgemeldet
MailSubjectTutorInvitation tid@TermId ssh@SchoolId csh@CourseShorthand tutn@TutorialName: [#{tid}-#{ssh}-#{csh}] Einladung zum Ausbilder für #{tutn}
TutorInviteHeading tutn@TutorialName: Einladung zum Ausbilder/zur Ausbilderin für #{tutn}
@ -49,4 +50,21 @@ TutorialUserGrantQualification: Qualifikation vergeben
TutorialUserRenewQualification: Qualifikation regulär verlängern
TutorialUserRenewedQualification n@Int: Qualifikation für #{tshow n} Kurs-#{pluralDE n "Teilnehmer:in" "Teilnehmer:innen"} regulär verlängert
TutorialUserGrantedQualification n@Int: Qualifikation erfolgreich an #{tshow n} Kurs-#{pluralDE n "Teilnehmer:in" "Teilnehmer:innen"} vergeben
CommTutorial: Kursmitteilung
TutorialUserAssignExam: Zur Prüfung einteilen
TutorialUserExamAssignedFor n@Int m@Int p@Text: #{n}/#{m} zur Prüfung #{p} eingeteilt
CommTutorial: Kursmitteilung
TutorialDrivingPermit: Führerschein
TutorialEyeExam: Sehtest
TutorialNote: Kursnotiz
TutorialDayAttendance day@Text: Anwesenheit #{day}
TutorialDayNote day@Text: Anwesenheitsnotiz #{day}
TutorialParticipantsDayEdits day@Text: Kursteilnehmer-Tagesnotizen aktualisiert für #{day}
PossibleCheckResults: Mögliche Prüfungsergebnisse
CheckEyePermitMissing: Sehtest oder Führerschein fehlen noch
CheckEyePermitIncompatible: Sehtest und Führerschein passen nicht zusammen
GenTutActOccCopyLast: Prüfungstermine von früherem Kurs kopieren
GenTutActOccCopyWeek: Prüfungstermine von früherer Woche kopieren
GenTutActOccEdit: Relevante Prüfungstermine bearbeiten
GenTutActShowExam: Prüfungsergebnisse der Kursteilnehmer anzeigen

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 Winnie Ros <winnie.ros@campus.lmu.de>
# SPDX-FileCopyrightText: 2022-25 Winnie Ros <winnie.ros@campus.lmu.de>,Steffen Jost <s.jost@fraport.de>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
@ -12,16 +12,16 @@ TutorialEdited tutn: Successfully edited course #{tutn}
TutorialEditHeading tutn: Edit #{tutn}
TutorEmail: Email
TutorialTutorAlreadyAdded: An user with this email address is already registered as instructor
TutorialNameTip: Needs to be unique within the course type
TutorialNameTip: Needs to be unique within the course category
TutorialTypePlaceholder: Tutorial, Driving lesson, ...
TutorialTypeTip: Only for informational purposes
TutorialRegGroupTip: Course type participants may only register for a maximum of one course per registration group. Courses that do not have a registration group are treated as being in different registration groups
TutorialRegGroupTip: Course category participants may only register for a maximum of one course per registration group. Courses that do not have a registration group are treated as being in different registration groups
TutorialRegGroup: Registration group
TutorialTutorControlled: Instructors may edit course
TutorialTutorControlledTip: Should instructors be allowed to edit arbitrary aspects of this course (name, registration group, room, time, other instructors, ...) at will?
TutorialCapacity: Capacity
TutorialCapacityNonPositive: Capacity may not be negative
TutorialCapacityTip: Limits how many course type participants may register for this course
TutorialCapacityTip: Limits how many course category participants may register for this course
TutorialRoomHiddenTip: Should the room only be displayed to course participants?
RegisterFrom: Enrolment starts
RegisterTo: Enrolment ends
@ -36,6 +36,7 @@ TutorialDelete: Delete
TutorialsHeading: Courses
TutorialNew: New course
TutorialRegisteredSuccess tutn: Successfully registered for the course #{tutn}
TutorialRegisteredFail tutn: Registering for the course #{tutn} failed. Probably already registered?
TutorialDeregisteredSuccess tutn: Successfully de-registered for the course #{tutn}
MailSubjectTutorInvitation tid ssh csh tutn: [#{tid}-#{ssh}-#{csh}] Invitation to be a instructor for #{tutn}
TutorInviteHeading tutn: Invitation to be instructor for #{tutn}
@ -50,4 +51,21 @@ TutorialUserGrantQualification: Grant qualification
TutorialUserRenewQualification: Renew qualification
TutorialUserRenewedQualification n@Int: Successfully renewed qualification #{tshow n} course #{pluralEN n "user" "users"}
TutorialUserGrantedQualification n: Successfully granted qualification #{tshow n} course #{pluralEN n "user" "users"}
TutorialUserAssignExam: Register for examination
TutorialUserExamAssignedFor n@Int m@Int p@Text: #{n}/#{m} enrolled for exam #{p}
CommTutorial: Course message
TutorialDrivingPermit: Driving permit
TutorialEyeExam: Eye exam
TutorialNote: Course note
TutorialDayAttendance day: Attendance #{day}
TutorialDayNote day: Attendance note #{day}
TutorialParticipantsDayEdits day: course participant day notes updated for #{day}
PossibleCheckResults: Possible results
CheckEyePermitMissing: Eye exam or driving permit missing
CheckEyePermitIncompatible: Eye exam and driving permit are incompatible
GenTutActOccCopyLast: Copy exam occurrences from previous course
GenTutActOccCopyWeek: Copy exam occurrences from course on previous week
GenTutActOccEdit: Edit relevant exam occurrences
GenTutActShowExam: Show exam results for course participants

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2023-24 Steffen Jost <s.jost@fraport.de>
# SPDX-FileCopyrightText: 2023-25 Steffen Jost <s.jost@fraport.de>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
@ -21,8 +21,11 @@ FirmActResetSupersKeepAll: Alle behalten
FirmActResetSupersRemoveAps: Nur Standardansprechpartner entfernen
FirmActResetSupersRemoveAll: Alle entfernen
FirmActAddSupervisors: Ansprechpartner hinzufügen
FirmActAddSupersEmpty: Es konnten keine Ansprechpartner hinzugefügt werden
FirmActAddAssociates: Firmenangehörige hinzufügen
FirmActAddSupersEmpty: Es konnten keine neuen 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.
FirmActAddAssocsEmpty: Es konnten keine neuen Firmenangehörige hinzugefügt werden!
FirmActAddAssocs n@Int64: #{n} Firmenangehörige hinzugefügt.
RemoveSupervisors ndef@Int64: #{ndef} Standardansprechpartner entfernt.
FirmActChangeContactUser: Kontaktinformationen von allen Firmenangehörigen ändern
FirmActChangeContactFirm: Kontaktinformationen der Firma ändern
@ -38,7 +41,7 @@ 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.
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 Klienten 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)}
@ -70,9 +73,25 @@ TableSuperior: Vorgesetzter
TableIsDefaultReroute: Standardumleitung
FormFieldPostal: Benachrichtigungseinstellung
FormFieldPostalTip: Gilt für alle Benachrichtigungen an diese Person, nicht nur für Umleitungen an diesen Ansprechpartner
FormFieldPinPass: Sensible PDF-E-Mail-Anhänge mit Passwort schützen?
FormFieldPinPassRemove: Passwortschutz für PDF-E-Mail-Anhänge entfernen?
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!
CompanySupervisorCompanyMissing fsh@CompanyShorthand: Empfänger ist nicht mit Firma #{fsh} aus Ansprechpartnerbeziehung assoziiert
CompanySuperviseeCompanyMissing fsh@CompanyShorthand: Klient ist nicht mit Firma #{fsh} aus Ansprechpartnerbeziehung assoziiert
FirmSupervisionRInfo: In folgenden Ansprechpartnerbeziehungen gehören entweder der Ansprechpartner oder der Klient nicht mehr der Firma an, welche als Begründung für die Beziehung eingetragen ist.
SupervisionViolationChoice: Firmenassoziation fehlt für
SupervisionViolationEither: Egal
SupervisionViolationSupervisor: Ansprechpartner
SupervisionViolationClient: Klient
SupervisionViolationBoth: Beide
SupervisionsRemoved n@Int64 m@Int64: #{n}/#{m} #{pluralDE n "Ansprechpartnerbeziehung" "Ansprechpartnerbeziehungen"} entfernt.
SupervisionsEdited n@Int64 m@Int64: #{n}/#{m} #{pluralDE n "Ansprechpartnerbeziehung" "Ansprechpartnerbeziehungen"} geändert.
ASChangeCompany: Begründungen für Ansprechpartnerbeziehung abändern
ASRemoveAssociation: Ansprechpartnerbeziehung löschen
FirmNameNotFound: Keine Firma mit diesen Namen/Kürzel/AVS-Nr gefunden.
FirmNameAmbiguous: Firmenname/-kürzel oder AVS-Nr ist nicht eindeutig.

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2023-24 Steffen Jost <s.jost@fraport.de>
# SPDX-FileCopyrightText: 2023-25 Steffen Jost <s.jost@fraport.de>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
@ -15,14 +15,17 @@ 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?
FirmActRemoveSupers: Terminate all company related supervisionships?
FirmActResetMutualSupervision: Supervisors supervise each other
FirmActResetSupersKeepAll: Keep all
FirmActResetSupersRemoveAps: Remove default supervisors only
FirmActResetSupersRemoveAll: Remove all
FirmActAddSupervisors: Add supervisors
FirmActAddSupersEmpty: No supervisors added
FirmActAddAssociates: Associate users with company
FirmActAddSupersEmpty: No new 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.
FirmActAddAssocsEmpty: No new company associated users added!
FirmActAddAssocs n: #{pluralENsN n "company associated user"} added.
RemoveSupervisors ndef: #{ndef} default supervisors removed.
FirmActChangeContactUser: Change contact data for all company associates
FirmActChangeContactFirm: Change company contact data
@ -70,9 +73,25 @@ TableSuperior: Superior
TableIsDefaultReroute: Default reroute
FormFieldPostal: Notification type
FormFieldPostalTip: Affects all notifications to this person, not just reroutes to this supervisor
FormFieldPinPass: Protect sensitive PDF e-mail attachments by password?
FormFieldPinPassRemove: Remove password protection for PDF e-mail attachments?
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!
CompanySupervisorCompanyMissing fsh: Receiver is not associated with #{fsh} given as reroute reason
CompanySuperviseeCompanyMissing fsh: Supervisee is not associated with #{fsh} detailed as supervisionship reason
FirmSupervisionRInfo: Shown are supervisionships where either supervisor or supervisee no longer belong to the company associated with the supervisionship.
SupervisionViolationChoice: Company association missing for
SupervisionViolationEither: anyone
SupervisionViolationSupervisor: Supervisor
SupervisionViolationClient: Supervisee
SupervisionViolationBoth: both
SupervisionsRemoved n m: #{n}/#{m} #{pluralENs n "Supervisionship"} removed.
SupervisionsEdited n m: #{n}/#{m} #{pluralENs n "Supervisionship"} edited.
ASChangeCompany: Change supervisionship annotations
ASRemoveAssociation: Delete supervisionship
FirmNameNotFound: No company found with this name/shorthand or AVS number.
FirmNameAmbiguous: Company name/shorthand or AVS number is amiguous.

View File

@ -6,7 +6,7 @@ HeadingLegal: Legal
InfoSupervisorTitle: Information for supervisors
InfoLecturerTitle: Information for course administrators
InfoLecturerCourses: Courses
InfoLecturerExercises: Course type exercises
InfoLecturerExercises: Course category exercises
InfoLecturerTutorials: Courses
InfoLecturerExams: Exams
LecturerInfoTooltipNew: New feature
@ -20,8 +20,8 @@ KnownBugs: Known bugs
ImplementationDetails: Implementation
Clone: Cloning
Administrator: Administrator
CommCourse: Course type message
CommCourse: Course category message
Corrector: Corrector
DefinitionCourseEvents: Course type occurrences
DefinitionCourseNews: Course type news
DefinitionCourseEvents: Course category occurrences
DefinitionCourseNews: Course category news
Invitations: Invitations

View File

@ -17,7 +17,7 @@ PrintJobReprint n@Int m@Int: #{n}/#{m} #{pluralDE n "Druckauftrag" "Druckaufräg
PrintJobAcknowledgeFailed: Keine Druckaufträge bestätigt aufgrund zwischenzeitlicher Änderungen. Bitte die Seite im Browser aktualisieren!
PrintJobAcknowledgeQuestion n@Int d@Text: #{n} #{pluralDE n "Druckauftrag" "Druckaufräge"} vom #{d} als gedruckt und versendet bestätigen?
PrintJobAcknowledgements: Versanddatum von Briefen an
PrintRecipient: Empfänger
PrintRecipient: Empfänger:innen
PrintAffected: Betroffener
PrintSender !ident-ok: Sender
PrintCourse: Kursarten
@ -28,5 +28,8 @@ PrintLmsUser: ELearning Id
PrintJobs: Druckaufräge
PrintLetterType: Brieftypkürzel
MCActDummy: Platzhalter
MCActResendEmail: EMail Kopie versenden
MCActResendEmailTooltip: Eine unveränderte Kopie der EMail erneut versenden. Nur die vorherigen Empfänger werden offiziell aufgeführt, sie erhalten jedoch keine neue Kopie.
MCActResendEmailInfo n@Int recv@Text: #{pluralDEnN n "EMail Kopie"} wurden an #{recv} versandt.
CCActDummy: Platzhalter

View File

@ -20,7 +20,7 @@ PrintJobAcknowledgements: Sent-dates for Letter to
PrintRecipient: Recipient
PrintAffected: Affetcted
PrintSender: Sender
PrintCourse: Course type
PrintCourse: Course category
PrintQualification: Qualification
PrintPDF: PDF
PrintManualRenewal: Manual sending of an apron driver's licence renewal letter
@ -28,5 +28,8 @@ PrintLmsUser: Elearning id
PrintJobs: Print jobs
PrintLetterType: Letter type shorthand
MCActDummy: Placeholder
MCActResendEmail: Resend email copy
MCActResendEmailTooltip: Resend an unchanged copy of the email. Only previous recipients will officially be listed, but they will not receive another copy.
MCActResendEmailInfo n recv: #{n} #{noneOneMoreEN n "email copy" "email copy" "email copies"} were sent to #{recv} only.
CCActDummy: Placeholder

View File

@ -1,26 +1,30 @@
# SPDX-FileCopyrightText: 2022 Steffen Jost <jost@cip.ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>
# SPDX-FileCopyrightText: 2022-25 Steffen Jost <jost@cip.ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
QualificationShort: Kürzel
QualificationName: Qualifikation
QualificationDescription: Beschreibung
QualificationValidReason qsh@Text: #{qsh} Gültigkeit
QualificationValidIndicator: Gültigkeit
QualificationValidDuration: Gültigkeitsdauer
QualificationAuditDuration: Aufbewahrung Audit Log
QualificationAuditDurationTooltip n@Int: Optionaler Zeitraum zur Löschung von ELearning Daten. Hinweis: Der ELearning Server kann seine anonymisierten Daten schon früher löschen, aber spätestens #{n} Tage nach Abschluss.
QualificationAuditDurationReuseError: Diese Qualifikation nutzt das ELearning einer anderen Qualifikation, für die derzeit keinen Löschzeitraum konfiguriert wurde.
QualificationAuditDuration: Aufbewahrungszeitraum ELearning Log
QualificationAuditDurationTooltip: Anzahl Tage zur Löschung von ELearning Daten. Hinweis: Der ELearning Server kann seine anonymisierten Daten schon früher löschen.
QualificationAuditDurationReuseInfo: Aufbewahrungszeitraum ELearning Log wird ignoriert, da das ELearning einer anderen Qualifikation mitbenutzt wird.
QualificationRefreshWithin: Erneurerungszeitraum
QualificationRefreshWithinTooltip: Optionaler Zeitraum vor Ablauf für eine Benachrichtigung per Email. Bei aktiviertem automatischem ELearning wird dieses gestartet und die Benachrichtigung erfolgt per Brief oder Email.
QualificationRefreshReminder: Zweite Erinnerung
QualificationRefreshReminderTooltip: Optionaler Zeitraum vor Ablauf zur Versendung einer zweiten Erinnerung per Brief oder Email mit identischen ELearning Zugangsdaten, sofern die Qualifikation noch gültig und das ELearning noch offen ist.
QualificationElearningStart: Wird das ELearning automatisch gestartet?
QualificationElearningRenew: Verlängert ein erfolgreiches ELearning die Qualifikation automatisch um die reguläre Gültigkeitsdauer?
QualificationElearningLimit: Ist die Anzahl der ELearning Versuche limitiert?
QualificationElearningLimit: Limit Anzahl ELearning Versuche
QualificationElearningLimitExplain: Ist die Anzahl der ELearning Versuche limitiert?
QualificationElearningLimitMax n@Int: Maximal #{n} Versuche
QualificationElearningNoLimit: Nicht limitiert
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 nicht deaktiviert hat.
QualificationAvsLicence: AVS Qualifikation
QualificationSapId: SAP Qualifikations Id
TableQualificationCountActive: Aktive
TableQualificationCountActiveTooltip: Anzahl Personen mit momentan gültiger Qualifikation
TableQualificationCountTotal: Gesamt
@ -70,7 +74,7 @@ TableLmsNotified: Versand Benachrichtigung
TableLmsNotifiedTooltip: Benachrichtigungen werden erst versendet wenn das LMS bestätigt die Eröffnung des ELearning für den Benutzer bestätigt hat, was ein paar Stunden dauern kann!
TableLmsEnded: Beendet
TableLmsStatus: Status ELearning
TableLmsStatusTooltip mbMonth@(Maybe Int): Zeigt #{maybeToMessage "bis zu " (fmap (flip pluralDEeN "Monat") mbMonth) " nach Abschluss"} den letzten Zustand eines ELearnings an:
TableLmsStatusTooltip n@Int: Zeigt bis zu #{pluralDEeN n "Tag"} nach Abschluss den letzten Zustand eines ELearnings an:
TableLmsStatusDay: Datum letzte Statusänderung ELearning
TableLmsSuccess: Bestanden
TableLmsLock: Gesperrt
@ -80,6 +84,7 @@ LmsStatusExpired: Durchgefallen nach Fristablauf
LmsStatusSuccess: E#{nonBreakableDash}Learning bestanden
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.
FilterLmsLongValid: Längerfristig gültig
FilterLmsValid: Aktuell gültig
FilterLmsRenewal: Erneuerung anstehend
FilterLmsNotified: Benachrichtigt
@ -98,8 +103,8 @@ LmsReportInsert: Neues LMS Ereignis
LmsReportUpdate: LMS Ereignis Aktualisierung
LmsReportCsvExceptionDuplicatedKey: CSV-Import LmsReport fand uneindeutigen Schlüssel
LmsDirectUpload: Direkter Upload für automatisierte Systeme
LmsErrorNoRefreshElearning: Fehler: ELearning wird nicht automatisch gestartet, da die Zeitspanne für den Erneurerungszeitraum nicht festgelegt wurde!
LmsErrorNoRenewElearning: Fehler: Erfoglreiches ELearning verlängert die Qualifikation nicht automatisch, da die Gültigkeitsdauer nicht festgelegt wurde!
LmsErrorNoRefreshElearning: Fehler: ELearning wird nicht automatisch gestartet, da die Zeitspanne für den Erneuerungszeitraum nicht festgelegt wurde!
LmsErrorNoRenewElearning: Fehler: Erfolgreiches ELearning verlängert die Qualifikation nicht automatisch, da die Gültigkeitsdauer nicht festgelegt wurde!
MailSubjectQualificationRenewal qname@Text: Qualifikation #{qname} muss demnächst erneuert werden
MailSubjectQualificationExpiry qname@Text: Qualifikation #{qname} läuft demnächst ab
MailSubjectQualificationExpired qname@Text: Qualifikation #{qname} ist ab sofort ungültig
@ -116,7 +121,7 @@ QualificationActBlock: Entziehen
QualificationActUnblock: Entzug aufheben
QualificationActRenew: Qualifikation regulär verlängern
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; ELearning wird ggf. beendet.
QualificationActStartELearning: ELearning für gültige Inhaber (neu) starten
QualificationActStartELearningStatus l@QualificationShorthand n@Int m@Int: ELearning #{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
@ -126,8 +131,7 @@ LmsRenewalInstructions: Weitere Anweisungen zur Verlängerung finden Sie im ange
LmsNoRenewal: Leider kann diese Qualifikation nicht alleine durch ELearning 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
LmsActNotify: Benachrichtigung ELearning erneut per Post oder E-Mail versenden
LmsActRenewPin: Neues zufällige ELearning Passwort zuweisen
LmsActRenewNotify: Neue zufällige ELearning Passwort zuweisen und Benachrichtigung per Post oder E-Mail versenden
LmsActRenewNotify: Neues zufälliges ELearning Passwort zuweisen und Benachrichtigung per Post oder E-Mail versenden
LmsActReset: ELearning Fehlversuche zurücksetzen und entsperren
LmsActResetInfo: ELearning 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.
LmsActResetFeedback n@Int m@Int: Für #{n}/#{m} ELearning Nutzer wurden alle Fehlversuche zurückgesetzt.
@ -136,6 +140,11 @@ LmsActRestartWarning: Das vorhandene ELearning wird komplett gelöscht! Für
LmsActRestartFeedback n@Int m@Int: #{n}/#{m} ELearning Nutzer wurden komplett neu gestartet mit neuem Login und Passwort.
LmsActRestartExtend: Gültig bis ggf. erhöhen für die nächsten # Tage
LmsActRestartUnblock: Entzug ggf. aufheben
LmsActTerminate: ELearning abbrechen
LmsActTerminateInfo: Ein späterer automatischer Neustart des ELearning wird dadurch nicht verhindert, wenn eine gültige Qualifikation bald abläuft und ELearning für diese Qualifikation generell automatisch startet.
LmsActTerminateFeedback n@Int m@Int: #{n}/#{m} ELearning Nutzer wurden zur Löschung freigegeben.
LmsActTerminated n@Int: ELearning für #{n} Nutzer wurde beendet.
LmsActTerminateWarning: ACHTUNG: Ein Ergebnis würde ohne Warnung verworfen, sollte ein Nutzer sein ELearning absolvieren, bevor die Löschung beim Elearning Server effektiv wurde.
LmsStateOpen: ELearning offen
LmsStatusLocked: ELearning gesperrt, wird ggf. bald geöffnet
LmsStatusUnlocked: ELearning offen, wird ggf. bald gesperrt
@ -147,3 +156,18 @@ LmsActionFailed n@Int: Aktion nicht durchgeführt für #{n} #{pluralDE n "Person
LmsStarted: ELearning eröffnet
BtnLmsEnqueue: Nutzer mit ablaufenden Qualifikationen zum ELearning anmelden und benachrichtigen
BtnLmsDequeue: Nutzer mit beendetem ELearning aufräumen und ggf. benachrichtigen
QualificationEditNote: Hinweis: Die Änderungen treten sofort in Kraft. Bitte vergewissern Sie sich vorher, dass alles korrekt eingestellt wurde!
QualificationCreated qsh@Text: Qualifikation #{qsh} wurde angelegt.
QualificationEdit qsh@Text: Qualifikation #{qsh} wurde geändert.
QualFormErrorDuplShort qsh@Text: Es gibt bereits eine Qualifikation mit Kürzel #{qsh}!
QualFormErrorDuplName qname@Text: Es gibt bereits eine Qualifikation mit Namen #{qname}!
QualFormErrorSshMismatch: Qualifikationänderungsformular enthält unglültige Bereichsangabe. Bitte versuchen Sie erneut, nachdem Sie Seite neu geladen haben.
LmsOrphans: Verwaiste Logins
LmsOrphanNr n@Int: #{n} verwaiste ELearning Logins für diese Qualifikation erkannt.
LmsOrphanSeenFirst: Zuerst erkannt
LmsOrphanSeenLast: Zuletzt erhalten
LmsOrphanDeletedLast: Zuletzt Löschung beantragt
LmsOrphanReason: Bemerkung
LmsOrphanPreviewFltr: Löschungen bei nächstem Abruf anfordern?

View File

@ -1,26 +1,30 @@
# SPDX-FileCopyrightText: 2022 Steffen Jost <jost@cip.ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>
# SPDX-FileCopyrightText: 2022-25 Steffen Jost <jost@cip.ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
QualificationShort: Shorthand
QualificationName: Qualification
QualificationDescription: Description
QualificationValidReason qsh: #{qsh} Validity
QualificationValidIndicator: Validity
QualificationValidDuration: Validity period
QualificationAuditDuration: Audit log retention period
QualificationAuditDurationTooltip n@Int: Optional period for deletion of elearning data. Note that the elearning server may delete its anonymised data earlier, at most #{n} days after closing.
QualificationAuditDurationReuseError: This qualification reuses the elearning from another qualification, which has no audit duration configured.
QualificationAuditDurationTooltip: Days for deletion of elearning data. Note that the elearning server may delete its anonymised data earlier.
QualificationAuditDurationReuseInfo: Elearning audit log retention period ignore, since the elearning from another qualification is reused.
QualificationRefreshWithin: Refresh within
QualificationRefreshWithinTooltip: Optional period before expiry to send a notification by email. If elearning is set to start automatically, it will be started and elearning credentials are send with this notification by post or email.
QualificationRefreshReminder: Second reminder
QualificationRefreshReminderTooltip: Optional period before expiry to send a second notification by post or email once more, including the existing credentials, provided that the elearning is still undecided and the qualification has not yet expired.
QualificationElearningStart: Is elearning automatically started?
QualificationElearningRenew: Does successful elearning automatically extend a qualification by the default validity period?
QualificationElearningLimit: Is the number of elearning attempts limited?
QualificationElearningLimit: Limit of elearning attempts
QualificationElearningLimitExplain: Is the number of elearning attempts limited?
QualificationElearningLimitMax n: #{n} attempts maximum
QualificationElearningNoLimit: No limit
QualificationExpiryNotification: Invalidity notification?
QualificationExpiryNotificationTooltip: Qualification holder are notfied upon invalidity, provided they have activated such notification in their user settings.
QualificationExpiryNotification: Notification upon invalidation?
QualificationExpiryNotificationTooltip: Qualification holder are notfied upon invalidation, provided they have not deactivated such notification in their user settings.
QualificationAvsLicence: AVS Qualification
QualificationSapId: SAP Qualification Id
TableQualificationCountActive: Active
TableQualificationCountActiveTooltip: Number of currently valid qualification holders
TableQualificationCountTotal: Total
@ -67,10 +71,10 @@ TableLmsStaff: Staff?
TableLmsStarted: Started
TableLmsReceived: Last update
TableLmsNotified: Notification sent
TableLmsNotifiedTooltip: Notfications are not sent before the LMS acknowledges the opening of the elearning course type for the user, which may take several hours!
TableLmsNotifiedTooltip: Notfications are not sent before the LMS acknowledges the opening of the elearning course category for the user, which may take several hours!
TableLmsEnded: Ended
TableLmsStatus: Status elearning
TableLmsStatusTooltip mbMonth: Shows #{maybeToMessage "for up to " (fmap (flip pluralENsN "month") mbMonth) " after closure"} the last e#{nonBreakableDash}learning status change:
TableLmsStatusTooltip n: Shows for up to #{pluralENsN n "day"} after closure the last e#{nonBreakableDash}learning status change:
TableLmsStatusDay: Date of last elearning status change
TableLmsSuccess: Completed
TableLmsLock: Locked
@ -80,6 +84,7 @@ LmsStatusExpired: Failed due to expiry
LmsStatusSuccess: Passed
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.
FilterLmsLongValid: Long-term valid
FilterLmsValid: Currently valid
FilterLmsRenewal: Renewal due
FilterLmsNotified: Notified
@ -116,7 +121,7 @@ QualificationActBlock: Revoke
QualificationActUnblock: Clear revocation
QualificationActRenew: Renew 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; any elearning terminated
QualificationActStartELearning: Manually (re)start elearning for valid qualification holders
QualificationActStartELearningStatus l n m: Elearning #{l} (re)started for #{n}/#{m} users. Note: It may take a while, until the elearning is activated.
QualificationStatusBlock l n m: #{n}/#{m} #{l} revoked
@ -126,7 +131,6 @@ LmsRenewalInstructions: Instruction on how to accomplish the renewal are enclose
LmsNoRenewal: Unfortunately, this particular qualification cannot be renewed through elearning 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
LmsActNotify: Resend elearning notification by post or email
LmsActRenewPin: Randomly replace elearning password
LmsActRenewNotify: Randomly replace elearning password and re-send notification by post or email
LmsActReset: Reset and unlock elearning
LmsActResetInfo: Elearning 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.
@ -135,7 +139,12 @@ LmsActRestart: Restart elearning
LmsActRestartWarning: The existing elearning 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
LmsActRestartUnblock: Undo any revocations
LmsActRestartFeedback n@Int m@Int: #{n}/#{m} e-learnings were completely restarted with new login credentials.
LmsActRestartFeedback n m: #{n}/#{m} e-learnings were completely restarted with new login credentials.
LmsActTerminate: Abort elearning
LmsActTerminateInfo: Elearning may restart later, if a valid qualification is about to expire and e-learning starting automatically for this qualification.
LmsActTerminateFeedback n m: #{n}/#{m} elearnings marked for termination.
LmsActTerminated n: #{n} elearnings were terminated.
LmsActTerminateWarning: WARNING: Results will be discarded without warning if a user completes their e-learning in the meantime, before the deletion became effective on the elearning server.
LmsStateOpen: Elearning open
LmsStatusLocked: Elearning locked, may be opened soon
LmsStatusUnlocked: Elearning still open, may be locked soon
@ -147,3 +156,18 @@ LmsActionFailed n: No action for #{n} #{pluralENs n "person"}, since there was n
LmsStarted: Elearning open since
BtnLmsEnqueue: Enqueue users with expiring qualifications for elearning and notify them
BtnLmsDequeue: Dequeue users with finished elearning and notify failed users
QualificationEditNote: Changes are effective immediately. Please double check that all settings are correct before submitting the changes!
QualificationCreated qsh@Text: Qualification #{qsh} created.
QualificationEdit qsh@Text: Qualification #{qsh} edited.
QualFormErrorDuplShort qsh@Text: There already exists a qualification with shorthand #{qsh}!
QualFormErrorDuplName qname@Text: There already exists a qualification with name #{qname}!
QualFormErrorSshMismatch: Qualification edit form department mismatch. Please try again after reloading the page.
LmsOrphans: Orphaned logins
LmsOrphanNr n@Int: #{n} orphaned elearning login detected for this qualification.
LmsOrphanSeenFirst: First seen
LmsOrphanSeenLast: Last seen
LmsOrphanDeletedLast: Deletion requested
LmsOrphanReason: Note
LmsOrphanPreviewFltr: Deletion request next synch?

View File

@ -40,4 +40,6 @@ SchoolAuthorshipStatementSheetDefinitionTip: Bitte in sowohl deutscher als auch
SchoolAuthorshipStatementSheetExamDefinition: Eigenständigkeitserklärung für prüfungszugehörige Übungsblattabgaben
SchoolAuthorshipStatementSheetExamDefinitionTip: Bitte in sowohl deutscher als auch englischer Sprache angeben.
SchoolAuthorshipStatementSheetAllowOther: Abweichende Eigenständigkeitserklärungen für nicht-prüfungszugehörige Übungsblätter erlauben?
SchoolAuthorshipStatementSheetExamAllowOther: Abweichende Eigenständigkeitserklärungen für prüfungszugehörige Übungsblätter erlauben?
SchoolAuthorshipStatementSheetExamAllowOther: Abweichende Eigenständigkeitserklärungen für prüfungszugehörige Übungsblätter erlauben?
DailyActDummy: Platzhalter ohne Funktion

View File

@ -26,7 +26,7 @@ SchoolCreated ssh: Successfully created #{ssh}
SchoolExists ssh: A department named „#{ssh}“ already exists
SchoolAdmin: Admin
SchoolLecturer: Lecturer
SchoolEvaluation: Course type evaluation
SchoolEvaluation: Course category evaluation
SchoolExamOffice: Exam office
SchoolAuthorshipStatementSection: Statements of Authorship
@ -40,4 +40,6 @@ SchoolAuthorshipStatementSheetDefinitionTip: Please enter both german and englis
SchoolAuthorshipStatementSheetExamDefinition: Statement of Authorship for exam-related exercise sheets
SchoolAuthorshipStatementSheetExamDefinitionTip: Please enter both german and english statements.
SchoolAuthorshipStatementSheetAllowOther: Allow adaptations for exam-unrelated exercise sheets?
SchoolAuthorshipStatementSheetExamAllowOther: Allow adaptations for exam-related exercise sheets?
SchoolAuthorshipStatementSheetExamAllowOther: Allow adaptations for exam-related exercise sheets?
DailyActDummy: Placholder without function

View File

@ -9,17 +9,17 @@ MailCorrectionsTitle: Assigned corrections
#correctionsNotDistributed.hs + templates
MailSubjectSubmissionsUnassigned csh sheetName: Corrections for #{sheetName} of #{csh} could not be distributed
MailSubmissionsUnassignedIntro n courseName termDesc sheetName: #{n} corrections for #{sheetName} of the course type #{courseName} (#{termDesc}) could not be automatically distributed.
MailSubmissionsUnassignedIntro n courseName termDesc sheetName: #{n} corrections for #{sheetName} of the course category #{courseName} (#{termDesc}) could not be automatically distributed.
#courseRegistered.hs + templates
MailSubjectCourseRegistered csh: You were enrolled for #{csh}
MailSubjectCourseRegisteredOther displayName csh: #{displayName} was enrolled for #{csh}
MailCourseRegisteredIntro courseName termDesc: You were enrolled for the course type “#{courseName}” (#{termDesc})
MailCourseRegisteredIntroOther displayName courseName termDesc: #{displayName} was enrolled for the course type “#{courseName}” (#{termDesc}).
MailCourseRegisteredIntro courseName termDesc: You were enrolled for the course category “#{courseName}” (#{termDesc})
MailCourseRegisteredIntroOther displayName courseName termDesc: #{displayName} was enrolled for the course category “#{courseName}” (#{termDesc}).
#examActive.hs + templates
MailSubjectExamRegistrationActive csh examn: Registration is now allowed for #{examn} of #{csh}
MailExamRegistrationActiveIntro courseName termDesc examn: You may now register for #{examn} of the course type #{courseName} (#{termDesc}).
MailExamRegistrationActiveIntro courseName termDesc examn: You may now register for #{examn} of the course category #{courseName} (#{termDesc}).
MailSubjectExamRegistrationSoonInactive csh examn: The registration period for #{examn} of #{csh} ends shortly
MailExamRegistrationSoonInactiveIntro courseName termDesc examn: Soon you will no longer be allowed to register for #{examn} of #{courseName} (#{termDesc}).
MailSubjectExamDeregistrationSoonInactive csh examn: Deregistration for #{examn} in #{csh} ends shortly
@ -27,15 +27,15 @@ MailExamDeregistrationSoonInactiveIntro courseName termDesc examn: Soon you will
#examOffice.hs + templates
MailSubjectExamOfficeExamResults coursen examn: Results for #{examn} of #{coursen} are now available
MailExamOfficeExamResultsIntro courseName termDesc examn: A course administrator has made the results for #{examn} of the course type #{courseName} (#{termDesc}) available.
MailExamOfficeExamResultsIntro courseName termDesc examn: A course administrator has made the results for #{examn} of the course category #{courseName} (#{termDesc}) available.
MailSubjectExamOfficeExamResultsChanged coursen examn: Results for #{examn} of #{coursen} were changed
MailExamOfficeExamResultsChangedIntro courseName termDesc examn: A course administrator has changed exam results for #{examn} of the course type #{courseName} (#{termDesc}).
MailExamOfficeExamResultsChangedIntro courseName termDesc examn: A course administrator has changed exam results for #{examn} of the course category #{courseName} (#{termDesc}).
MailSubjectExamOfficeExternalExamResults coursen@CourseName examn@ExamName: Results for #{examn} in #{coursen}
MailExamOfficeExternalExamResultsIntro coursen@CourseName termDesc@Text examn@ExamName: A course administrator has changed or initially made available the results for #{examn} of the course type {coursen} (#{termDesc}).
MailExamOfficeExternalExamResultsIntro coursen@CourseName termDesc@Text examn@ExamName: A course administrator has changed or initially made available the results for #{examn} of the course category {coursen} (#{termDesc}).
#examOffice.hs + templates
MailSubjectExamResult csh examn: Results for #{examn} in #{csh} are now available
MailExamResultIntro courseName termDesc examn: You may now view your result for #{examn} of the course type #{courseName} (#{termDesc}).
MailExamResultIntro courseName termDesc examn: You may now view your result for #{examn} of the course category #{courseName} (#{termDesc}).
#sheetActive.hs + templates
MailSubjectSheetActive csh sheetName: #{sheetName} in #{csh} was released
@ -47,10 +47,10 @@ MailSheetSolutionIntro courseName termDesc sheetName: You may now download the s
#sheetInactive.hs + templates
MailSubjectSheetSoonInactive csh sheetName: The submission period for #{sheetName} of #{csh} ends shortly
MailSheetSoonInactiveIntro courseName termDesc sheetName: Soon you will no longer be allowed to submit for #{sheetName} of the course type #{courseName} (#{termDesc}).
MailSheetSoonInactiveIntro courseName termDesc sheetName: Soon you will no longer be allowed to submit for #{sheetName} of the course category #{courseName} (#{termDesc}).
MailSubjectSheetInactive csh sheetName: The submission period for #{sheetName} of #{csh} has ended
MailSheetInactiveIntro courseName termDesc sheetName n num: The submission period for #{sheetName} of the course type #{courseName} (#{termDesc}) has ended. #{noneOneMoreEN num "" "One participant" (toMessage num <> " participants")}#{noneOneMoreEN n "" "" (" made " <> toMessage num)}#{noneOneMoreEN n "There were no submissions" " made one submission" " submissions"}.
MailSheetInactiveIntroNoUserSubmission courseName termDesc sheetName n num: The submission period for #{sheetName} of the course type #{courseName} (#{termDesc}) has ended. #{noneOneMoreEN num "" "One participant already" (toMessage num <> " participants already")}#{noneOneMoreEN n "" "" (" made " <> toMessage num)}#{noneOneMoreEN n "" " made one submission" " submissions"}.
MailSheetInactiveIntro courseName termDesc sheetName n num: The submission period for #{sheetName} of the course category #{courseName} (#{termDesc}) has ended. #{noneOneMoreEN num "" "One participant" (toMessage num <> " participants")}#{noneOneMoreEN n "" "" (" made " <> toMessage num)}#{noneOneMoreEN n "There were no submissions" " made one submission" " submissions"}.
MailSheetInactiveIntroNoUserSubmission courseName termDesc sheetName n num: The submission period for #{sheetName} of the course category #{courseName} (#{termDesc}) has ended. #{noneOneMoreEN num "" "One participant already" (toMessage num <> " participants already")}#{noneOneMoreEN n "" "" (" made " <> toMessage num)}#{noneOneMoreEN n "" " made one submission" " submissions"}.
MailSheetInactivePseudonymsCount n: The number of submissions above accounts only for the submissions already made directly in FRADrive. #{n} #{pluralEN n "pseudonym was" "pseudonyms were"} generated.
MailSheetInactiveParticipantsCount n: There #{pluralEN n "is" "are"} currently #{n} #{pluralEN n "participant" "participants"} registered for the course.

View File

@ -14,7 +14,7 @@ AuthTagAdmin: User is administrator
AuthTagExamOffice: User is part of an exam office
AuthTagSystemExamOffice: User is charged with system wide exam administration
AuthTagSystemPrinter: User is responsible for system wide letter printing
AuthTagEvaluation: User is charged with course type evaluation
AuthTagEvaluation: User is charged with course category evaluation
AuthTagToken: User is presenting an authorisation-token
AuthTagNoEscalation: User permissions are not being expanded to other departments
AuthTagDeprecated: Page is not deprecated
@ -26,7 +26,7 @@ AuthTagTutor: User is instructor
AuthTagTutorControl: Instructors have control over their course
AuthTagTime: Time restrictions are fulfilled
AuthTagStaffTime: Time restrictions for teaching staff are fulfilled
AuthTagCourseTime: Time restrictions for course type visibility are fulfilled
AuthTagCourseTime: Time restrictions for course category visibility are fulfilled
AuthTagCourseRegistered: User is enrolled in course
AuthTagTutorialRegistered: User is course participant
AuthTagExamRegistered: User is exam participant
@ -37,11 +37,11 @@ AuthTagParticipant: User participates in course
AuthTagRegisterGroup: User is not participant in any course of the same registration group
AuthTagCapacity: Capacity is sufficient
AuthTagEmpty: Resource is “empty”
AuthTagMaterials: Course type material is publicly accessable
AuthTagMaterials: Course category material is publicly accessable
AuthTagOwner: User is owner
AuthTagPersonalisedSheetFiles: User has been assigned personalised sheet files
AuthTagRated: Submission is marked
AuthTagUserSubmissions: Submissions are made by course type participants
AuthTagUserSubmissions: Submissions are made by course category participants
AuthTagCorrectorSubmissions: Submissions are registered by correctors
AuthTagCorrectionAnonymous: Correction is anonymised
AuthTagSelf: User is only accessing their only data

View File

@ -31,7 +31,7 @@ DownloadFilesTip: When set, files are automatically treated as downloads. Otherw
WarningDays: Deadline-preview
WarningDaysTip: How many days ahead should deadlines regarding exams etc. be displayed on the homepage?
ShowSex: Show sex of other users
ShowSexTip: Should users' sex be displayed in (among others) lists of course type participants?
ShowSexTip: Should users' sex be displayed in (among others) lists of course category participants?
PDFPassword: Password to lock PDF email attachments
PDFPasswordTip: Please note that this password is displayed to FRADrive admins and is saved unencrypted
@ -53,14 +53,14 @@ UserSchoolsTip: You will only receive department-wide notifications for the sele
NotificationSettings: Desired notifications
NotificationTriggerKindAll: For all users
NotificationTriggerKindCourseParticipant: For course type participants
NotificationTriggerKindCourseParticipant: For course category participants
NotificationTriggerKindExamParticipant: For exam participants
NotificationTriggerKindCorrector: For correctors
NotificationTriggerKindLecturer: For course administrators
NotificationTriggerKindCourseLecturer: For course administrators
NotificationTriggerKindAdmin: For administrators
NotificationTriggerKindExamOffice: For the exam office
NotificationTriggerKindEvaluation: For course type evaluations
NotificationTriggerKindEvaluation: For course category evaluations
NotificationTriggerKindSubmissionUser: For participants in an exercise sheet submission
NotificationTriggerSubmissionRatedGraded: My submission for an exercise sheet was marked (not purely informational)

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>,Winnie Ros <winnie.ros@campus.lmu.de>
# SPDX-FileCopyrightText: 2022-25 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>,Winnie Ros <winnie.ros@campus.lmu.de>,Steffen Jost <s.jost@fraport.de>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
@ -19,6 +19,7 @@ ProfileSubmissionGroups: Abgabegruppen
ProfileSubmissions: Abgaben
ProfileRemark: Hinweis
ProfileQualifications: Eigene Qualifikationen
ProfileEnrolledExams: Angemeldete Prüfungen
PersonalInfoExamAchievementsWip: Die Anzeige von Prüfungsergebnissen wird momentan an dieser Stelle leider noch nicht unterstützt.
PersonalInfoOwnTutorialsWip: Die Anzeige von Kurse, zu denen Sie als Ausbilder eingetragen sind wird momentan an dieser Stelle leider noch nicht unterstützt.
PersonalInfoTutorialsWip: Die Anzeige von Kurse, zu denen Sie angemeldet sind wird momentan an dieser Stelle leider noch nicht unterstützt.
@ -37,5 +38,6 @@ ProfileSuperviseeRemark n@Int m@Int: Dieser Nutzer ist Ansprechpartner für #{n}
UserTelephone: Telefon
UserMobile: Mobiltelefon
Company: Firmenzugehörigkeit
CompanyPersonalNumber: Personalnummer (nur Fraport AG)
CompanyPersonalNumber: Personalnummer
CompanyPersonalNumberFraport: Personalnummer (nur Fraport AG)
CompanyDepartment: Abteilung

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 Steffen Jost <jost@tcs.ifi.lmu.de>,Winnie Ros <winnie.ros@campus.lmu.de>
# SPDX-FileCopyrightText: 2022-25 Steffen Jost <jost@tcs.ifi.lmu.de>,Winnie Ros <winnie.ros@campus.lmu.de>,Steffen Jost <s.jost@fraport.de>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
@ -11,7 +11,7 @@ LastLogin: Last login
NeverSet: Never
ProfileCorrector: Corrector
ProfileCourses: Own courses
ProfileCourseParticipations: Course type registrations
ProfileCourseParticipations: Course category registrations
ProfileCourseExamResults: Exam achievements
ProfileTutorials: Own courses
ProfileTutorialParticipations: Courses
@ -19,6 +19,7 @@ ProfileSubmissionGroups: Submission groups
ProfileSubmissions: Submissions
ProfileRemark: Remarks
ProfileQualifications: Owned Qualifications
ProfileEnrolledExams: Enrolled Exams
PersonalInfoExamAchievementsWip: The feature to display your exam achievements has not yet been implemented.
PersonalInfoOwnTutorialsWip: The feature to display courses you have been assigned to as instructor has not yet been implemented.
PersonalInfoTutorialsWip: The feature to display courses you have registered for has not yet been implemented.
@ -36,6 +37,7 @@ ProfileSuperviseeRemark n m: This person supervises #{pluralENsN n "person"}#{no
UserTelephone: Phone
UserMobile: Mobile
Company: Company affilitaion
CompanyPersonalNumber: Personnel number (Fraport AG only)
Company: Company affiliation
CompanyPersonalNumber: Personnel number
CompanyPersonalNumberFraport: Personnel number (Fraport AG only)
CompanyDepartment: Department

View File

@ -43,6 +43,7 @@ SynchroniseAvsAllUsersQueued n@Int64: AVS-Synchronisation von allen #{n} #{plura
SynchroniseUserdbUserQueued n@Int: Benutzerdatenbank-Synchronisation von #{n} #{pluralDE n "Benutzer:in" "Benutzer:innen"} angestoßen, die Ausführung wird mehrere Minuten benötigen!
SynchroniseUserdbAllUsersQueued: Benutzerdatenbank-Synchronisation von allen Benutzer:innen angestoßen, die Ausführung kann eine Weile brauchen!
UserListTitle: Komprehensive Benutzerliste
UserRecipientsTitle name@Text: Benachrichtigungsempfänger für #{name}
AccessRightsSaved: Berechtigungen erfolgreich verändert
AccessRightsNotChanged: Berechtigungen wurden nicht verändert
AuthLDAPLookupFailed: Nutzer:in konnte aufgrund eines LDAP-Fehlers nicht nachgeschlagen werden
@ -96,7 +97,7 @@ UserHijack: Sitzung übernehmen
UserAddSupervisor: Ansprechpartner hinzufügen
UserSetSupervisor: Ansprechpartner ersetzen
UserRemoveSupervisor: Alle Ansprechpartner entfernen
UserRemoveSubordinates: Alle Ansprechpartnerbeziehungen zu Untergebenen beenden
UserRemoveClients: Alle Ansprechpartnerbeziehungen zu Klienten beenden
UserIsSupervisor: Ist Ansprechpartner
UserAvsSwitchCompany: Als Primärfirma verwenden
UserAvsSwitchCompanyField: Primärfirma auswählen
@ -109,11 +110,12 @@ Name !ident-ok: Name
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!
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.
UsersRemoveClients 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.
UserSupervisorCompany: Ansprechpartner wegen Firma
AdminUserAllNotifications: Alle Benachrichtigungen and diesen Benutzer
AdminUserAuthentication: Authentification
AdminUserAuthLastSync: Zuletzt synchronisiert

View File

@ -43,6 +43,7 @@ SynchroniseAvsAllUsersQueued n: Triggered AVS synchronisation of all #{n} #{plur
SynchroniseUserdbUserQueued n: Triggered user database synchronisation of #{n} #{pluralEN n "user" "users"}, which may take several minutes to complete.
SynchroniseUserdbAllUsersQueued: Triggered user database synchronisation of all users, which may take quite a while to complete.
UserListTitle: Comprehensive list of users
UserRecipientsTitle name: Notificationrecipients for #{name}
AccessRightsSaved: Successfully updated permissions
AccessRightsNotChanged: Permissions left unchanged
AuthLDAPLookupFailed: User could not be looked up due to a LDAP error
@ -96,7 +97,7 @@ UserHijack: Hijack session
UserAddSupervisor: Add supervisor
UserSetSupervisor: Replace supervisors
UserRemoveSupervisor: Set to unsupervised
UserRemoveSubordinates: Remove all subordinates
UserRemoveClients: Remove all clients
UserIsSupervisor: Is supervisor
UserAvsSwitchCompany: Use as primary company
UserAvsSwitchCompanyField: Select primary company
@ -109,11 +110,12 @@ Name: Name
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!
UsersRemoveSupervisors usr: Removed all supervisors for #{pluralENsN usr "user"}.
UsersRemoveSubordinates usr: Removed all subordinates for #{pluralENsN usr "previous supervisor"}.
UsersRemoveClients usr: Removed all clients 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.
UserSupervisorCompany: Supervisor for company
AdminUserAllNotifications: All notification sent to this user
AdminUserAuthentication: Authentifizierung

View File

@ -5,6 +5,7 @@
#messages or constructors that are used all over the code
Logo !ident-ok: FRADrive
LdapIdentificationOrEmail: Fraport AG-Kennung / E-Mail-Adresse
EmailInvitationWarning: Diese Adresse konnte keinem FRADrive-Benutzer/-Benutzerin zugeordnet werden. Es wird eine Einladung per E-Mail versandt.
BoolIrrelevant !ident-ok: —
FieldPrimary: Hauptfach
@ -13,7 +14,8 @@ MultiEmailFieldTip: Es sind mehrere, Komma-separierte, E-Mail-Adressen möglich
MultiSelectTip: Mehrfachauswahl und Abwählen mit Strg-Klick
WeekDay: Wochentag
Hours: Stunden
LdapIdentificationOrEmail: Fraport AG-Kennung / E-Mail-Adresse
SomeMonths: Monate
SomeDays: Tage
Months num@Int64: #{num} #{pluralDE num "Monat" "Monate"}
Days num@Int64: #{num} #{pluralDE num "Tag" "Tage"}
NoAutomaticUpdateTip: Dieser Wert wurde manuell editiert und wird daher nicht mehr automatisch durch as AVS aktualisiert.
@ -31,4 +33,13 @@ PaginationPage: Angzeigte Seite
PaginationError: Paginierung Parameter dürfen nicht negativ sein
NullDeletes: Zum Löschen NULL eingeben.
SortPriority: Sortierungspriorität
SortPriority: Sortierungspriorität
NoProblem: Keine Probleme gefunden
Unknown: ist unbekannt
UnknownOrNotAllowed: ist unbekannt oder hier nicht erlaubt
Ambiguous: ist uneindeutig
Action: Aktion
For: für
Address: Adresse
NoContactAddress: Keinerlei Kontaktdaten bekannt!
StarKeepsEmptyDeletes: Stern zum Beibehalten, leer lassen zum Löschen

View File

@ -5,6 +5,7 @@
#messages or constructors that are used all over the Code
Logo: FRADrive
LdapIdentificationOrEmail: Fraport AG-Kennung / email address
EmailInvitationWarning: This address could not be matched to any FRADrive user. An invitation will be sent via email.
BoolIrrelevant: —
FieldPrimary: Major
@ -13,7 +14,8 @@ MultiEmailFieldTip: Multiple emails addresses may be specified (comma-separated)
MultiSelectTip: Multiple selection and desection via Ctrl-Click
WeekDay: Day of the week
Hours: Hours
LdapIdentificationOrEmail: Fraport AG-Kennung / email address
SomeMonths: Months
SomeDays: Days
Months num: #{num} #{pluralEN num "Month" "Months"}
Days num: #{num} #{pluralEN num "Day" "Days"}
NoAutomaticUpdateTip: This particular value receives no automatic AVS updates, since it has been edited manually.
@ -31,4 +33,13 @@ PaginationPage: Page to show
PaginationError: Pagination parameter must not be negative
NullDeletes: Enter NULL to delete.
SortPriority: Sort order priority
SortPriority: Sort order priority
NoProblem: No Probleme found
Unknown: is unknown
UnknownOrNotAllowed: is unknown or not allowed here
Ambiguous: is ambiguous
Action: Action
For: for
Address: Address
NoContactAddress: No contact details known!
StarKeepsEmptyDeletes: A star to keep unchanged, blank removes

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>,Winnie Ros <winnie.ros@campus.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>
# SPDX-FileCopyrightText: 2022-24 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>,Winnie Ros <winnie.ros@campus.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <s.jost@fraport.de>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
@ -55,6 +55,8 @@ BtnUserAssimilate: Assimilieren
BtnCloseExam: Prüfung abschließen
BtnFinishExam: Prüfungsergebnisse sichtbar schalten
BtnConfirm: Bestätigen
BtnPerform: Ausführen
BtnCourseRegisterAdd: Personen suchen
BtnCourseRegisterConfirm: Ausgewählte Personen anmelden
BtnCourseRegisterAbort: Abbrechen
BtnCourseRegisterAbort: Abbrechen
BtnCloseReload: Schließen und aktualisieren

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 Steffen Jost <jost@tcs.ifi.lmu.de>,Winnie Ros <winnie.ros@campus.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>
# SPDX-FileCopyrightText: 2022-24 Steffen Jost <jost@tcs.ifi.lmu.de>,Winnie Ros <winnie.ros@campus.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <s.jost@fraport.de>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
@ -55,6 +55,8 @@ BtnUserAssimilate: Assimilate
BtnCloseExam: Close exam
BtnFinishExam: Make results visible
BtnConfirm: Confirm
BtnPerform: Perform
BtnCourseRegisterAdd: Search persons
BtnCourseRegisterConfirm: Register selected persons
BtnCourseRegisterAbort: Abort
BtnCourseRegisterAbort: Abort
BtnCloseReload: Close and reload

View File

@ -20,3 +20,7 @@ ExceptionNoOccurAt: Termin
ExceptionKind: Termin ...
ExceptionKindOccur: Findet statt
ExceptionKindNoOccur: Findet nicht statt
DayNext: Folgetag
DayPrev: Vortag
WeekNext: Nächste Woche
WeekPrev: Vorherige Woche

View File

@ -20,3 +20,7 @@ ExceptionNoOccurAt: Event
ExceptionKind: Event ...
ExceptionKindOccur: Does occur
ExceptionKindNoOccur: Does not occur
DayNext: Next day
DayPrev: Previous day
WeekNext: Next week
WeekPrev: Previous week

View File

@ -71,6 +71,7 @@ BreadcrumbError: Fehler
BreadcrumbUpload !ident-ok: Upload
BreadcrumbUserAdd: Benutzer:in anlegen
BreadcrumbUserNotifications: Benachrichtigungs-Einstellungen
BreadcrumbUserRecipients: Benachrichtigungs-Empfänger
BreadcrumbUserPassword: Passwort
BreadcrumbAdminHeading !ident-ok: Administration
BreadcrumbAdminFeaturesHeading: Studiengänge

View File

@ -6,7 +6,7 @@ BreadcrumbCsvOptions: csv-options
BreadcrumbSubmissionFile: File
BreadcrumbSubmissionUserInvite: Invitation to participate in a submission
BreadcrumbCryptoIDDispatch: CryptoID-redirect
BreadcrumbCourseNotes: Course type notes
BreadcrumbCourseNotes: Course category notes
BreadcrumbHiWis: Correctors
BreadcrumbMaterial: Material
BreadcrumbSheet: Exercise sheet
@ -14,7 +14,7 @@ BreadcrumbTutorial: Course
BreadcrumbExam: Exam
BreadcrumbCourseRegister: Register
BreadcrumbCourseFavourite: Favourite
BreadcrumbCourse: Course type
BreadcrumbCourse: Course category
BreadcrumbTerm: Year
BreadcrumbSchool: Department
BreadcrumbUser: User
@ -28,11 +28,11 @@ BreadcrumbUserDelete: Delete user account
BreadcrumbUserHijack: Hijack user session
BreadcrumbSystemMessage: System message
BreadcrumbSubmission: Submission
BreadcrumbCourseNews: Course type news
BreadcrumbCourseNewsDelete: Delete course type news
BreadcrumbCourseEventDelete: Delete course type occurrence
BreadcrumbCourseNews: Course category news
BreadcrumbCourseNewsDelete: Delete course category news
BreadcrumbCourseEventDelete: Delete course category occurrence
BreadcrumbProfile: Settings
BreadcrumbCourseParticipantInvitation: Invitation to be a course type participant
BreadcrumbCourseParticipantInvitation: Invitation to be a course category participant
BreadcrumbMaterialArchive: Archive
BreadcrumbMaterialFile: File
BreadcrumbMaterialVideo: Video
@ -57,8 +57,8 @@ BreadcrumbExternalExamUsers: Participants
BreadcrumbExternalExamGrades: Exam results
BreadcrumbExternalExamStaffInvite: Invitation
BreadcrumbExternalExamCorrect: Enter exam results
BreadcrumbParticipantsList: Lists of course type participants
BreadcrumbParticipants: Course type participants
BreadcrumbParticipantsList: Lists of course category participants
BreadcrumbParticipants: Course category participants
BreadcrumbExamAutoOccurrence: Automatic occurrence/room distribution
BreadcrumbStorageKey: Generate storage key
BreadcrumbMessageHide: Hide
@ -71,6 +71,7 @@ BreadcrumbError: Error
BreadcrumbUpload: Upload
BreadcrumbUserAdd: Add user
BreadcrumbUserNotifications: Notification settings
BreadcrumbUserRecipients: Notification recipients
BreadcrumbUserPassword: Password
BreadcrumbAdminHeading: Administration
BreadcrumbAdminFeaturesHeading: Features of study
@ -97,7 +98,7 @@ BreadcrumbTermShow: Years
BreadcrumbTermCreate: Create new year
BreadcrumbTermEdit: Edit year
BreadcrumbTermCurrent: Current year
BreadcrumbParticipantsIntersect: Common course type participants
BreadcrumbParticipantsIntersect: Common course category participants
BreadcrumbCourseList: Courses
BreadcrumbCourseNew: Create new course
BreadcrumbCourseEdit: Edit course
@ -106,14 +107,14 @@ BreadcrumbCourseAddMembers: Add participants
BreadcrumbCourseExamOffice: Exam offices
BreadcrumbCorrectionsAssign: Assign corrections
BreadcrumbSheetList: Exercise sheets
BreadcrumbCourseCommunication: Course type message (email)
BreadcrumbCourseCommunication: Course category message (email)
BreadcrumbTutorialList: Courses
BreadcrumbTutorialNew: Create new course
BreadcrumbCourseDelete: Delete course
BreadcrumbCourseNewsNew: Add course type news
BreadcrumbCourseNewsEdit: Edit course type news
BreadcrumbCourseEventNew: New course type occurrence
BreadcrumbCourseEventEdit: Edit course type occurrence
BreadcrumbCourseNewsNew: Add course category news
BreadcrumbCourseNewsEdit: Edit course category news
BreadcrumbCourseEventNew: New course category occurrence
BreadcrumbCourseEventEdit: Edit course category occurrence
BreadcrumbExamList: Exams
BreadcrumbExamNew: Create new exam
BreadcrumbExamEdit: Edit exam
@ -123,7 +124,7 @@ BreadcrumbExamAddMembers: Add exam participants
BreadcrumbExamCorrect: Grade exams
BreadcrumbTutorialDelete: Delete course
BreadcrumbTutorialEdit: Edit course
BreadcrumbTutorialComm: Send course type message
BreadcrumbTutorialComm: Send course category message
BreadcrumbSheetEdit: Edit exercise sheet
BreadcrumbSheetDelete: Delete exercise sheet
BreadcrumbSubmissions: Submissions

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>, David Mosbach <david.mosbach@uniworx.de>, 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>,Winnie Ros <winnie.ros@campus.lmu.de>
# SPDX-FileCopyrightText: 2022-25-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>, David Mosbach <david.mosbach@uniworx.de>, 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>,Winnie Ros <winnie.ros@campus.lmu.de>,Steffen Jost <s.jost@fraport.de>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
@ -34,6 +34,7 @@ MenuCourseIcon: Kurse
MenuCourseMembers: Kursartteilnehmer:innen
MenuCourseAddMembers: Kursartteilnehmer:innen hinzufügen
MenuTutorialAddMembers: Kursteilnehmer:innen hinzufügen
MenuTutorialExam exn@ExamName: Kursprüfung #{exn} bearbeiten
MenuCourseCommunication: Kursartmitteilung (EMail)
MenuCourseExamOffice: Prüfungsbeauftragte
MenuTermShow: Jahr
@ -88,6 +89,7 @@ MenuTutorialComm: Mitteilung an Teilnehmer:innen
MenuExamList: Prüfungen
MenuExamNew: Neue Prüfung anlegen
MenuExamEdit: Prüfung bearbeiten
MenuExamEditComplete: Prüfung vollständig überarbeiten
MenuExamUsers: Teilnehmer:innen
MenuExamGrades: Prüfungsleistungen
MenuExamAddMembers: Prüfungsteilnehmer hinzufügen
@ -98,6 +100,8 @@ MenuExamOfficeUsers: Benutzer:innen
MenuLecturerInvite: Funktionäre hinzufügen
MenuSchoolList: Bereiche
MenuSchoolNew: Neuen Bereich anlegen
MenuSchoolDay ssh@SchoolId d@Text: #{d} #{unSchoolKey ssh} Tagesansicht
MenuSchoolDayCheck: Konsistenzprüfung
MenuExternalExamGrades: Prüfungsleistungen
MenuExternalExamUsers: Teilnehmer:innen
MenuExternalExamEdit: Bearbeiten
@ -120,8 +124,9 @@ MenuCourseEventEdit: Kursarttermin bearbeiten
MenuLanguage: Sprache
MenuQualifications: Qualifikationen
MenuQualificationEdit: Bearbeiten
MenuQualificationNew: Neue Qualifikation erstellen
MenuLms !ident-ok: ELearning
MenuLmsEdit: Bearbeiten ELearning
MenuLmsUser: Benutzerqualifikationen
MenuLmsUserSchool: Bereichs Benutzerqualifikationen
MenuLmsUserAll: Alle Benutzerqualifikationen
@ -137,6 +142,7 @@ MenuFirms: Firmen
MenuFirmUsers: Angehörige
MenuFirmSupervisors: Ansprechpartner
MenuFirmsComm: Mitteilung
MenuFirmsSupervision: Probleme Ansprechpartnerbeziehungen
MenuInterfaces: Schnittstellen
MenuSap: SAP Schnittstelle

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>, David Mosbach <david.mosbach@uniworx.de>, Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>,Winnie Ros <winnie.ros@campus.lmu.de>
# SPDX-FileCopyrightText: 2022-25-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>, David Mosbach <david.mosbach@uniworx.de>, Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>,Winnie Ros <winnie.ros@campus.lmu.de>,Steffen Jost <s.jost@fraport.de>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
@ -6,7 +6,7 @@ MenuAdminHeading: Administration
MenuAdminFeaturesHeading: Features of study
MenuInfoLecturerTitle: Information for course administrators
MenuInfoLecturerCourses: Courses
MenuInfoLecturerExercises: Course type Exercises
MenuInfoLecturerExercises: Course category Exercises
MenuInfoLecturerTutorials: Courses
MenuInfoLecturerExams: Exams
MenuCsvOptions: CSV-options
@ -29,12 +29,13 @@ MenuAccount: Account
MenuProfile: Settings
MenuLogin: Login
MenuLogout: Logout
MenuCourseList: Course types
MenuCourseList: Course categorys
MenuCourseIcon: Courses
MenuCourseMembers: Participants
MenuCourseAddMembers: Add course type participants
MenuCourseAddMembers: Add course category participants
MenuTutorialAddMembers: Add course participants
MenuCourseCommunication: Course type message (email)
MenuTutorialExam exn@ExamName: Edit course exam #{exn}
MenuCourseCommunication: Course category message (email)
MenuCourseExamOffice: Exam offices
MenuTermShow: Semesters
MenuSubmissionDelete: Delete submission
@ -49,7 +50,7 @@ MenuAdminErrMsg: Decrypt error message
MenuAdminTokens: Issue tokens
MenuProfileData: Personal information
MenuTermCreate: Create new year
MenuCourseNew: Create new course type
MenuCourseNew: Create new course category
MenuTermEdit: Edit year
MenuTermCurrent: Current year
MenuCorrection: Correction
@ -84,10 +85,11 @@ MenuCorrectionsAssignSheet name: Assign corrections for #{name}
MenuAuthPreds: Authorisation settings
MenuTutorialDelete: Delete course
MenuTutorialEdit: Edit course
MenuTutorialComm: Send course type message
MenuTutorialComm: Send course category message
MenuExamList: Exams
MenuExamNew: Create new exam
MenuExamEdit: Edit exam
MenuExamEditComplete: Revise entire exam
MenuExamUsers: Participants
MenuExamGrades: Exam results
MenuExamAddMembers: Add exam participants
@ -98,14 +100,16 @@ MenuExamOfficeUsers: Users
MenuLecturerInvite: Add functionaries
MenuSchoolList: Departments
MenuSchoolNew: Create new department
MenuSchoolDay ssh d: #{d} #{unSchoolKey ssh} Agenda
MenuSchoolDayCheck: Consistence check
MenuExternalExamGrades: Exam results
MenuExternalExamUsers: Participants
MenuExternalExamEdit: Edit
MenuExternalExamNew: New external exam
MenuExternalExamList: External exams
MenuExternalExamCorrect: Enter exam results
MenuParticipantsList: Lists of course type participants
MenuParticipantsIntersect: Common course type participants
MenuParticipantsList: Lists of course category participants
MenuParticipantsIntersect: Common course category participants
MenuFaq: FAQ
MenuSheetPersonalisedFiles: Download personalised sheet files
MenuCourseSheetPersonalisedFiles: Download template for personalised sheet files
@ -113,17 +117,18 @@ MenuAdminCrontab: Crontab
MenuAdminJobs: Job queue
MenuGlossary: Glossary
MenuVersion: Version history
MenuCourseNewsNew: Add course type news
MenuCourseNewsEdit: Edit course type news
MenuCourseEventNew: New course type occurrence
MenuCourseEventEdit: Edit course type occurrence
MenuCourseNewsNew: Add course category news
MenuCourseNewsEdit: Edit course category news
MenuCourseEventNew: New course category occurrence
MenuCourseEventEdit: Edit course category occurrence
MenuLanguage: Language
MenuQualifications: Qualifications
MenuQualificationEdit: Edit
MenuQualificationNew: Create new qualification
MenuLms: Elearning
MenuLmsEdit: Edit elearning
MenuLmsUser: User Qualifications
MenuLmsUserSchool: Institute User Qualifications
MenuLmsUserSchool: Department User Qualifications
MenuLmsUserAll: All User Qualifications
MenuLmsUsers: Legacy download elearning users
MenuLmsUpload: Upload
@ -137,6 +142,7 @@ MenuFirms: Companies
MenuFirmUsers: Associates
MenuFirmSupervisors: Supervisors
MenuFirmsComm: Messaging
MenuFirmsSupervision: Problems supervisionship
MenuInterfaces: Interfaces
MenuSap: SAP Interface

View File

@ -12,8 +12,8 @@ NewsHeading: Aktuelles
InfoHeading: Informationen
LegalHeading: Rechtliche Informationen
VersionHeading: Versionsgeschichte
SystemMessageHeading: Uni2work Statusmeldung
SystemMessageListHeading: Uni2work Statusmeldungen
SystemMessageHeading: FRADrive Statusmeldung
SystemMessageListHeading: FRADrive Statusmeldungen
HeadingHelpRequest: Supportanfrage/Verbesserungsvorschlag
ProfileHeading: Benutzereinstellungen
ProfileDataHeading: Gespeicherte Benutzerdaten

View File

@ -12,8 +12,8 @@ NewsHeading: News
InfoHeading: Information
LegalHeading: Legal
VersionHeading: Version history
SystemMessageHeading: Uni2work system message
SystemMessageListHeading: Uni2work system message
SystemMessageHeading: FRADrive system message
SystemMessageListHeading: FRADrive system messages
HeadingHelpRequest: Support request/Suggestion
ProfileHeading: Settings
ProfileDataHeading: Personal information
@ -29,8 +29,8 @@ HeadingTermEditTid tid: Edit year #{tid}
TermCourseListHeading tid: Courses #{tid}
TermSchoolCourseListHeading tid school: Courses #{tid}, #{school}
CourseListTitle: All courses
CourseNewHeading: Create new course type
CourseEditHeading tid ssh csh: Edit course type #{tid}-#{ssh}-#{csh}
CourseNewHeading: Create new course category
CourseEditHeading tid ssh csh: Edit course category #{tid}-#{ssh}-#{csh}
SubmissionsCourse tid ssh csh: All submissions for Course #{tid}-#{ssh}-#{csh}
SubmissionsSheet sheetName: Submissions for #{sheetName}
SheetList tid ssh csh : #{tid}-#{ssh}-#{csh} Sheet Overview
@ -38,7 +38,7 @@ SheetNewHeading tid ssh csh : #{tid}-#{ssh}-#{csh} New Exercise Sheet
SheetTitle tid@TermId ssh@SchoolId csh@CourseShorthand sheetName@SheetName: #{tid}-#{ssh}-#{csh} #{sheetName}
SheetTitleNew tid@TermId ssh@SchoolId csh@CourseShorthand : #{tid}-#{ssh}-#{csh}: New sheet
SheetEditHead tid ssh csh sheetName: #{tid}-#{ssh}-#{csh} Edit #{sheetName}
SheetDelHead tid ssh csh sheetName: Do you really want to delete sheet #{sheetName} from course type #{tid}-#{ssh}-#{csh}? Any associated submissions and corrections will be lost!
SheetDelHead tid ssh csh sheetName: Do you really want to delete sheet #{sheetName} from course category #{tid}-#{ssh}-#{csh}? Any associated submissions and corrections will be lost!
SubmissionEditHead tid ssh csh sheetName: #{tid}-#{ssh}-#{csh} #{sheetName}: Edit/Create submission
CorrectionHead tid ssh csh sheetName cid: #{tid}-#{ssh}-#{csh} #{sheetName}: Marking
CorrectionsTitle: Assigned corrections
@ -48,8 +48,8 @@ CorrGrade: Mark submissions
TableHeadingCsvImport: CSV import
TableHeadingCsvExport: CSV export
FavouritesEmptyTip: Your courses and recently visited courses are shown here.
FavouritesToggleTip: The display mode for the current course type can be changed between automatic, permanent and never with a click on the star symbol.
FavouritesUnavailableTip: Quick Actions for this course type are currently not available.
FavouritesToggleTip: The display mode for the current course category can be changed between automatic, permanent and never with a click on the star symbol.
FavouritesUnavailableTip: Quick Actions for this course category are currently not available.
NavigationFavourites: Favourites
ErrorResponseTitleInternalError internalError: An internal error occurred
ErrorResponseTitleInvalidArgs invalidArgs: Request contained invalid arguments

View File

@ -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>,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>,Steffen Jost <jost@tcs.ifi.lmu.de>,Winnie Ros <winnie.ros@campus.lmu.de>, Steffen Jost <s.jost@fraport.de>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
@ -47,12 +47,12 @@ TablePassed: Bestanden
TableNotPassed: Nicht bestanden
TableTutorialTutors: Ausbilder
TableTutorialName: Bezeichnung
TableTutorialType: Art
TableTutorialRoom: Regulärer Raum
TableTutorialType: Typ
TableTutorialRoom: Raum
TableTutorialRoomHidden: Raum nur für Teilnehmer
TableTutorialRoomIsUnset !ident-ok: —
TableTutorialRoomIsHidden: Raum wird nur Teilnehmern angezeigt
TableTutorialTime: Zeit
TableTutorialOccurrence: Termin
TableTutorialDeregisterUntil: Abmeldungen bis
TableTutorialFirstDay: Starttag
TableActionsHead: Aktionen
@ -80,6 +80,9 @@ TableCompanyFilter: Firma oder Nummer
TableCompanyShort: Firmenkürzel
TableCompanies: Firmen
TablePrimeCompany: Primäre Firma
TablePrimeCompanyShort: Kürzel primäre Firma
TableBookingCompany: Buchende Firma
TableBookingCompanyShort: Kürzel buchende Firma
TableCompanyNo: Firmennummer
TableCompanyNos: Firmennummern
TableCompanyUser: Firmenangehöriger
@ -97,8 +100,10 @@ TableCompanyNrRerouteDefault: Standard Umleitungen
TableCompanyNrRerouteActive: Aktive Umleitungen
TableRerouteActive: Umleitung
TableCompanyPostalPreference: Benachrichtigungspräferenz neue Firmenangehörige
TableCompanyPinPassword: Pin Passwort für PDF Anhänge
TableSupervisor: Ansprechpartner
TableSupervisee: Ansprechpartner für
TableSupervisorActive: Aktiver Ansprechpartner
TableSupervisee: Klient
TableReason: Begründung
TableCreationTime: Erstellungszeit
TableJob !ident-ok: Job
@ -109,10 +114,18 @@ 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
ActJobSleep: Test Job einreihen
JobSleepNr: Anzahl Jobs
JobSleepSecs: Laufzeit in Sekunden pro Job
JobSleepNow: Prioriäts-Jobs
TableJobActSleepFeedback n@Int sec@Int prio@Bool: #{n} #{bool tempty "Prioritäts-" prio}#{pluralDEx 's' n "Job"} mit #{sec}s Laufzeit eingereiht.
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.
TableFilterCommaNameNr: Mehrere Namen oder exakte Nummern mit Komma trennen.
TableUserEdit: Benutzer bearbeiten
TableRows: Zeilen
TableRows: Zeilen
TableUserParkingToken day@Text: Parkmarke #{day}
TableFilterSentBefore: Gesendet bis
TableFilterSentAfter: Gesendet ab

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2022 Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>,Winnie Ros <winnie.ros@campus.lmu.de>
# SPDX-FileCopyrightText: 2022-24 Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>,Winnie Ros <winnie.ros@campus.lmu.de>, Steffen Jost <s.jost@fraport.de>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
@ -20,7 +20,7 @@ TableMatrikelNr: AVS person no
TableSex: Sex
TableBirthday: Birthday
TableSchool: Department
TableCourse: Course
TableCourse: Course category
TableCourseMembers: Participants
TableExamOccurrence: Occurrence/room
TableExamName: Name
@ -48,14 +48,14 @@ TableNotPassed: Failed
TableTutorialTutors: Instructors
TableTutorialName: Name
TableTutorialType: Type
TableTutorialRoom: Regular room
TableTutorialRoom: Room
TableTutorialRoomHidden: Room only for participants
TableTutorialRoomIsUnset: —
TableTutorialRoomIsHidden: Room is only displayed to participants
TableTutorialDeregisterUntil: Deregister until
TableTutorialFirstDay: Start date
TableActionsHead: Actions
TableTutorialTime: Time
TableTutorialOccurrence: Session
TableNoFilter: No restriction
TableUserMatriculation: AVS number
TableColumnStudyFeatures: Features of study
@ -80,6 +80,9 @@ TableCompanyFilter: Company/Nr
TableCompanyShort: Company shorthand
TableCompanies: Companies
TablePrimeCompany: Primary company
TablePrimeCompanyShort: Primary company shorthand
TableBookingCompany: Booking company
TableBookingCompanyShort: Booking company shorthand
TableCompanyNo: Company number
TableCompanyNos: Company numbers
TableCompanyUser: Associate
@ -97,8 +100,10 @@ TableCompanyNrRerouteDefault: Default reroutes
TableCompanyNrRerouteActive: Active reroutes
TableRerouteActive: Reroute
TableCompanyPostalPreference: Default notification preference
TableCompanyPinPassword: Pin password for PDF attachments
TableSupervisor: Supervisor
TableSupervisee: Supervisor for
TableSupervisorActive: Active supervisor
TableSupervisee: Supervisee
TableReason: Reason
TableCreationTime: Creation
TableJob !ident-ok: Job
@ -109,10 +114,18 @@ 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
ActJobSleep: Enqueue sleep job
JobSleepNr: Number of jobs
JobSleepSecs: Seconds per job
JobSleepNow: Priority jobs
TableJobActSleepFeedback n@Int sec@Int prio@Bool: #{n} #{bool tempty "priority " prio} sleep #{pluralENs n "job"} for #{sec}s enqueued.
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.
TableFilterCommaNameNr: Separate names and exact numbers by comma.
TableUserEdit: Edit user
TableRows: Rows
TableRows: Rows
TableUserParkingToken day: Parking token #{day}
TableFilterSentBefore: Sent before
TableFilterSentAfter: Sent after

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2023-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>, Steffen Jost <jost@tcs.ifi.lmu.de>, Gregor Kleen <gregor.kleen@ifi.lmu.de>, Sarah Vaupel <sarah.vaupel@ifi.lmu.de>, Winnie Ros <winnie.ros@campus.lmu.de>
# SPDX-FileCopyrightText: 2023-25-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>, Steffen Jost <jost@tcs.ifi.lmu.de>, Gregor Kleen <gregor.kleen@ifi.lmu.de>, 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
@ -81,6 +81,7 @@ MultiUserFieldExplanationAnyUser: Dieses Eingabefeld sucht in den Adressen aller
MultiUserFieldInvitationExplanation: An Adressen, die so keinem Uni2work-Benutzer/keiner Uni2work-Benutzerin zugeordnet werden können, wird eine Einladung per E-Mail versandt.
MultiUserFieldInvitationExplanationAlways: Es wird an alle Adressen, die Sie hier angeben, eine Einladung per E-Mail versandt.
AmbiguousEmail: E-Mail-Adresse nicht eindeutig
UnknownEmail: E-Mail-Adresse konnte keinem bekannten Benutzer zugeordnet werden
InvalidEmailAddress: E-Mail-Adresse ist ungültig
InvalidEmailAddressWith e@Text: E-Mail-Adresse #{show e} ist ungültig
MailFileAttachment: Dateianhang
@ -91,6 +92,7 @@ UtilExamResultVoided: Entwertet
CourseOption tid@TermId ssh@SchoolId csh@CourseShorthand coursen@CourseName !ident-ok: #{tid} - #{ssh} - #{csh}: #{coursen}
RoomReferenceNone !ident-ok: —
RoomReferenceSimple !ident-ok: Text
RoomReferenceSimpleAt r@Text: in Raum #{r}
RoomReferenceLink: Link & Anweisungen
RoomReferenceSimpleText: Raum
RoomReferenceSimpleTextPlaceholder: Raum
@ -102,6 +104,7 @@ UtilNoneSet: Keine angegeben
UtilEmptyChoice: Auswahl war leer
UtilEmptyNoChangeTip: Eine leere Eingabe belässt den vorherigen Wert unverändert.
MultiNoSelection: Keine Auswahl
MustBePositive: muss positiv sein
#invitation.hs
InvitationAction: Aktion

View File

@ -1,18 +1,18 @@
# SPDX-FileCopyrightText: 2023-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>, Sarah Vaupel <sarah.vaupel@ifi.lmu.de>, Winnie Ros <winnie.ros@campus.lmu.de>, Steffen Jost <jost@tcs.ifi.lmu.de>
# SPDX-FileCopyrightText: 2023-25-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>, Sarah Vaupel <sarah.vaupel@ifi.lmu.de>, Winnie Ros <winnie.ros@campus.lmu.de>, Steffen Jost <jost@tcs.ifi.lmu.de>,Steffen Jost <s.jost@fraport.de>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
#communication.hs
RecipientCustom: Custom recipients
RGCourseParticipants: Course type participants
RGCourseParticipants: Course category participants
RGCourseLecturers: Course administrators
RGCourseCorrectors: Course type correctors
RGCourseCorrectors: Course category correctors
RGCourseTutors: Course instructors
RGCourseParticipantsInTutorial: Course type participants who are registered for at least one course
RGCourseParticipantsInTutorial: Course category participants who are registered for at least one course
RGCourseUnacceptedApplicants: Applicants not accepted
RecipientToggleAll: All/None
CommCourseTestSubject customSubject: [TEST] #{customSubject}
UtilCommCourseSubject: Course type message
UtilCommCourseSubject: Course category message
UtilCommFirmSubject: Company message
CommRecipients: Recipients
CommRecipientsTip: You always receive a copy of the message
@ -27,7 +27,7 @@ RGSheetSubmittor shn: Submitted for exercise sheet “#{shn}”
CommSubject: Subject
CommContent: Content
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 category 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 category at a later date.
CommSuccess n: Message was sent to #{n} #{pluralEN n "recipient" "recipients"}
CommTestSuccess: Message was sent only to yourself for testing purposes
@ -81,6 +81,7 @@ MultiUserFieldExplanationAnyUser: This input searches through the addresses of a
MultiUserFieldInvitationExplanation: For addresses, which are not found in this way, an invitation will be sent via email.
MultiUserFieldInvitationExplanationAlways: An invitation will be sent via email to all addresses you enter here.
AmbiguousEmail: Email address is ambiguous
UnknownEmail: Email adresse is not associated with any registred user
InvalidEmailAddress: Email address is invalid
InvalidEmailAddressWith e: Email asdress #{show e} is invalid
MailFileAttachment: Attached file
@ -91,6 +92,7 @@ UtilExamResultVoided: Voided
CourseOption tid ssh csh coursen: #{tid} - #{ssh} - #{csh}: #{coursen}
RoomReferenceNone: —
RoomReferenceSimple: Text
RoomReferenceSimpleAt r: at room #{r}
RoomReferenceLink: Link & Instructions
RoomReferenceSimpleText: Room
RoomReferenceSimpleTextPlaceholder: Room
@ -102,6 +104,7 @@ UtilNoneSet: None set
UtilEmptyChoice: Empty selection
UtilEmptyNoChangeTip: Existing values remain unchanged if this field is left empty.
MultiNoSelection: No selection
MustBePositive: must be positive
#invitation.hs
InvitationAction: Action

View File

@ -1,4 +1,4 @@
-- SPDX-FileCopyrightText: 2022-24 Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>
-- SPDX-FileCopyrightText: 2022-25 Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <s.jost@fraport.de>
--
-- SPDX-License-Identifier: AGPL-3.0-or-later
@ -11,8 +11,10 @@ Company
prefersPostal Bool default=true -- new company users prefers letters by post instead of email
postAddress StoredMarkup Maybe -- default company postal address, including company name
email UserEmail Maybe -- Case-insensitive generic company eMail address
pinPassword Bool default=true -- new company users only: should sensitive PDF email attachement be protected by a password?
-- UniqueCompanyName name -- Should be Unique in AVS, but we do not yet need to enforce it
-- UniqueCompanyShorthand shorthand -- unnecessary, since it is the primary key already
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

View File

@ -28,13 +28,12 @@ Course -- Information about a single course; contained info is always visible
TermSchoolCourseName term school name -- name must be unique within school and semester
deriving Generic
CourseEvent
type (CI Text)
course CourseId OnDeleteCascade OnUpdateCascade
room RoomReference Maybe
roomHidden Bool default=false
time Occurrences
note StoredMarkup Maybe
lastChanged UTCTime default=now()
type (CI Text)
course CourseId OnDeleteCascade OnUpdateCascade
roomHidden Bool default=false
time (JSONB Occurrences)
note StoredMarkup Maybe
lastChanged UTCTime default=now()
deriving Generic
CourseAppInstructionFile

View File

@ -37,16 +37,17 @@ ExamPart
UniqueExamPartName exam name !force
deriving Read Show Eq Ord Generic
ExamOccurrence
exam ExamId
name ExamOccurrenceName
room RoomReference Maybe
roomHidden Bool default=false
capacity Word64 Maybe
start UTCTime
end UTCTime Maybe
description StoredMarkup Maybe
exam ExamId
name ExamOccurrenceName
examiner UserId Maybe
room RoomReference Maybe
roomHidden Bool default=false
capacity Word64 Maybe
start UTCTime
end UTCTime Maybe
description StoredMarkup Maybe
UniqueExamOccurrence exam name
deriving Generic
deriving Eq Ord Show Generic Binary
ExamRegistration
exam ExamId
user UserId

View File

@ -1,30 +1,30 @@
-- SPDX-FileCopyrightText: 2022-23 Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>
-- SPDX-FileCopyrightText: 2022-25 Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <s.jost@fraport.de>
--
-- SPDX-License-Identifier: AGPL-3.0-or-later
Qualification
-- INVARIANT: 2*refreshWithin < validDuration
school SchoolId --TODO: Ansprechpartner der Schule in Briefe erwähnen
shorthand (CI Text)
name (CI Text)
description StoredMarkup Maybe -- user-defined large Html, ought to contain full description
validDuration Int Maybe -- > 0, qualification is valid indefinitely or for a specified number of months, use with addMonthsDay
auditDuration Int Maybe -- > 0, number of months to keep audit log and LmsUserIdents; or indefinitely (dangerous, since LmsIdents may run out)
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
elearningStart Bool -- automatically schedule e-refresher
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?
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
school SchoolId -- 1
shorthand (CI Text) -- 2
name (CI Text) -- 3
description StoredMarkup Maybe -- 4 user-defined large Html, ought to contain full description
validDuration Int Maybe -- 5 if > 0, qualification is valid indefinitely or for a specified number of months, use with addMonthsDay
auditDuration Int default=366 -- 6 number of days to keep LMS audit log and LmsUserIdents -- TODO: audit period for QualificationUser/Block as well
refreshWithin CalendarDiffDays Maybe -- 7 notify users about renewal within this number of month/days before expiry; to be used with addGregorianDurationClip
refreshReminder CalendarDiffDays Maybe -- 8 send a second notification about renewal within this number of month/days before expiry
elearningStart Bool -- 9 automatically schedule e-refresher
elearningRenews Bool default=true -- 10 successful e-learing automatically increases validity automatically by validDuration
elearningLimit Int Maybe -- 11 limit of e-learning attempts, currently only for informative purposes, as it is enforced by LMS only
lmsReuses QualificationId Maybe -- 12 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 -- 13 should expiryNotification be generated for this qualification?
avsLicence AvsLicence Maybe -- 14 if set, valid QualificationUsers are synchronized to AVS as a driving licence
sapId Text Maybe -- 15 if set, valid QualificationUsers with userCompanyPersonalNumber are transmitted via SAP interface under this id
SchoolQualificationShort school shorthand -- must be unique per school and shorthand
SchoolQualificationName school name -- must be unique per school and name
-- across all schools, only one qualification may be a driving licence -- NO LONGER TRUE
-- UniqueQualificationAvsLicence avsLicence !force -- either empty or unique
-- NOTE: two NULL values are not equal for the purpose of Uniqueness constraints!
deriving Show Eq Generic
deriving Show Eq Generic Binary
-- TODOs:
-- - Enstehen Kosten, wenn Teilnehmer für KnowHow eingereiht werden, aber nicht am Kurs teilnehmen?
@ -164,4 +164,16 @@ LmsReportLog
lock Bool -- (0|1)
timestamp UTCTime default=now()
missing Bool default=false
deriving Generic Show
-- Table to manage unknown or orphaned lms identifiers
LmsOrphan
qualification QualificationId OnDeleteCascade OnUpdateCascade
ident LmsIdent -- must be unique accross all LMS courses!
seenFirst UTCTime default=now() -- first time reported by LMS
seenLast UTCTime default=now() -- last acknowledgement by LMS, deletion uses QualificationAuditDuration
deletedLast UTCTime Maybe -- last deletion request sent to LMS
resultLast LmsState -- last received learning state
reason Text Maybe -- to mark explicit e-learning deletions, etc
UniqueLmsOrphan qualification ident -- unlike other tables, LMS Idents must only be unique within qualification, allowing orphans to be handled independently
deriving Generic Show

View File

@ -6,10 +6,9 @@ Tutorial json
name TutorialName
course CourseId OnDeleteCascade OnUpdateCascade
type (CI Text) -- "Tutorium", "Zentralübung", ...
capacity Int Maybe -- limit for enrolment in this tutorial
room RoomReference Maybe
capacity Int Maybe -- limit for enrolment in this tutorial
roomHidden Bool default=false
time Occurrences
time (JSONB Occurrences)
regGroup (CI Text) Maybe -- each participant may register for one tutorial per regGroup
registerFrom UTCTime Maybe
registerTo UTCTime Maybe
@ -25,8 +24,19 @@ Tutor
UniqueTutor tutorial user
deriving Generic
TutorialParticipant
tutorial TutorialId OnDeleteCascade OnUpdateCascade
user UserId
tutorial TutorialId OnDeleteCascade OnUpdateCascade
user UserId
company CompanyId Maybe
drivingPermit UserDrivingPermit Maybe
eyeExam UserEyeExam Maybe
note Text Maybe
UniqueTutorialParticipant tutorial user
deriving Eq Ord Show
deriving Generic
deriving Eq Ord Show Generic
TutorialParticipantDay
tutorial TutorialId OnDeleteCascade OnUpdateCascade
user UserId OnDeleteCascade OnUpdateCascade
day Day
attendance Bool default=true
note Text Maybe
UniqueTutorialParticipantDay tutorial user day
deriving Show Generic

View File

@ -114,4 +114,9 @@ UserSupervisor
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
UserDay
user UserId OnDeleteCascade OnUpdateCascade
day Day
parkingToken Bool default=false
UniqueUserDay user day
deriving Generic Show

30
package-lock.json generated
View File

@ -13577,11 +13577,7 @@
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
"dev": true
},
"node_modules/package-json-from-dist": {
"version": "1.0.1",
@ -17814,7 +17810,6 @@
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"dev": true,
"license": "0BSD",
"peer": true
},
"node_modules/tty-browserify": {
@ -18963,6 +18958,29 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"param-case": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
"integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==",
"extraneous": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"tslib": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
"integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==",
"extraneous": true
},
"tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
"extraneous": true,
"license": "MIT"
}
}
}

22
routes
View File

@ -1,4 +1,4 @@
-- 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-FileCopyrightText: 2022-2025 Sarah Vaupel <sarah.vaupel@uniworx.de>, 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
@ -61,6 +61,7 @@
/users/#CryptoUUIDUser/hijack AdminHijackUserR GET POST !adminANDno-escalation
/users/#CryptoUUIDUser/notifications UserNotificationR GET POST !self
/users/#CryptoUUIDUser/password UserPasswordR GET POST !selfANDis-pw-hash
/users/#CryptoUUIDUser/recipients UserRecipientsR GET !self
!/users/functionary-invite/new AdminNewFunctionaryInviteR GET POST
!/users/functionary-invite AdminFunctionaryInviteR GET POST
!/users/add AdminUserAddR GET POST
@ -129,6 +130,7 @@
/firms FirmAllR GET POST -- not yet !supervisor
/firms/comm/+Companies FirmsCommR GET POST
/firms/supervision FirmsSupervisionR 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
@ -161,8 +163,9 @@
/school SchoolListR GET
!/school/new SchoolNewR GET POST
/school/#SchoolId SchoolR:
/ SchoolEditR GET POST
/edit SchoolEditR GET POST
/day/#Day SchoolDayR GET POST
/day/#Day/check SchoolDayCheckR GET
/participants ParticipantsListR GET !evaluation
/participants/#TermId/#SchoolId ParticipantsR GET !evaluation
@ -232,6 +235,7 @@
/delete TDeleteR GET POST
/participants TUsersR GET POST !tutor
/participants/add TAddUserR GET POST !tutor
/participants/exam/#ExamName TExamR GET POST !tutor
/register TRegisterR POST !timeANDcapacityANDcourse-registeredANDregister-group !timeANDtutorial-registered
/communication TCommR GET POST !tutor
/tutor-invite TInviteR GET POST !tutorANDtutor-control
@ -281,20 +285,22 @@
!/#UUID CryptoUUIDDispatchR GET !free -- just redirect
-- !/*{CI FilePath} CryptoFileNameDispatchR GET !free -- Disabled until preliminary check for valid cID exists
/qualification QualificationAllR GET !free
/qualification/#SchoolId QualificationSchoolR GET !free
/qualification/#SchoolId/#QualificationShorthand QualificationR GET POST !free
/qualification QualificationAllR GET !free
/qualification/#SchoolId QualificationSchoolR GET !free
!/qualification/#SchoolId/new QualificationNewR GET POST -- not free
/qualification/#SchoolId/#QualificationShorthand QualificationR GET POST !free
/qualification/#SchoolId/#QualificationShorthand/edit QualificationEditR GET POST -- not free
-- /qualification/#SchoolId/#QualificationShorthand/#CryptoUUIDUser QualificationUserR GET -- see LmsUserR
/qualifications/sap/direct QualificationSAPDirectR GET -- !token -- SAP EXPORT -- TODO reinstate token requirement
/qualifications/sap/direct QualificationSAPDirectR GET -- !token -- SAP EXPORT -- TODO reinstate token requirement
-- LMS
/lms LmsAllR GET POST
/lms/#SchoolId LmsSchoolR GET
/lms/#SchoolId/#QualificationShorthand LmsR GET POST
/lms/#SchoolId/#QualificationShorthand/edit LmsEditR GET POST
-- new V2 LMS Interface
/lms/#SchoolId/#QualificationShorthand/learners LmsLearnersR GET
/lms/#SchoolId/#QualificationShorthand/learners/orphans LmsOrphansR GET
/lms/#SchoolId/#QualificationShorthand/learners/direct LmsLearnersDirectR GET !token -- LMS
/lms/#SchoolId/#QualificationShorthand/report LmsReportR GET POST
/lms/#SchoolId/#QualificationShorthand/report/upload LmsReportUploadR GET POST

View File

@ -1,4 +1,4 @@
-- SPDX-FileCopyrightText: 2022-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>, Gregor Kleen <gregor.kleen@ifi.lmu.de>, Sarah Vaupel <sarah.vaupel@ifi.lmu.de>, Sarah Vaupel <vaupel.sarah@campus.lmu.de>, Steffen Jost <jost@tcs.ifi.lmu.de>, David Mosbach <david.mosbach@uniworx.de>
-- SPDX-FileCopyrightText: 2022-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>,-2024 Gregor Kleen <gregor.kleen@ifi.lmu.de>, Sarah Vaupel <sarah.vaupel@ifi.lmu.de>, Sarah Vaupel <vaupel.sarah@campus.lmu.de>, Steffen Jost <jost@tcs.ifi.lmu.de>, David Mosbach <david.mosbach@uniworx.de>,Steffen Jost <s.jost@fraport.de>
--
-- SPDX-License-Identifier: AGPL-3.0-or-later
@ -114,13 +114,8 @@ import GHC.RTS.Flags (getRTSFlags)
import qualified Prometheus
import qualified Data.IntervalMap.Strict as IntervalMap
import qualified Utils.Pool as Custom
import Utils.Postgresql
import Handler.Utils.Memcached (manageMemcachedLocalInvalidations)
import qualified System.Clock as Clock
import Utils.Avs (mkAvsQuery)
@ -136,6 +131,7 @@ import Handler.Users.Add
import Handler.Admin
import Handler.Term
import Handler.School
import Handler.School.DayTasks
import Handler.Course
import Handler.Sheet
import Handler.Submission
@ -217,18 +213,6 @@ makeFoundation appSettings''@AppSettings{..} = do
appJobState <- liftIO newEmptyTMVarIO
appHealthReport <- liftIO $ newTVarIO Set.empty
appFileSourceARC <- for appFileSourceARCConf $ \ARCConf{..} -> do
ah <- initARCHandle arccMaximumGhost arccMaximumWeight
void . Prometheus.register $ arcMetrics ARCFileSource ah
return ah
appFileSourcePrewarm <- for appFileSourcePrewarmConf $ \PrewarmCacheConf{..} -> do
lh <- initLRUHandle precMaximumWeight
void . Prometheus.register $ lruMetrics LRUFileSourcePrewarm lh
return lh
appFileInjectInhibit <- liftIO $ newTVarIO IntervalMap.empty
for_ (guardOnM (isn't _JobsOffload appJobMode) appInjectFiles) $ \_ ->
void . Prometheus.register $ injectInhibitMetrics appFileInjectInhibit
appStartTime <- liftIO getCurrentTime
-- We need a log function to create a connection pool. We need a connection
-- pool to create our foundation. And we need our foundation to get a
@ -237,7 +221,7 @@ makeFoundation appSettings''@AppSettings{..} = do
-- from there, and then create the real foundation.
let
mkFoundation :: _ -> (forall m. MonadIO m => Custom.Pool' m DBConnLabel DBConnUseState SqlBackend) -> _
mkFoundation appSettings' appConnPool appSmtpPool appLdapPool appCryptoIDKey appSessionStore appSecretBoxKey appWidgetMemcached appJSONWebKeySet appClusterID appMemcached appMemcachedLocal appUploadCache appVerpSecret appAuthKey appAuthPlugins appPersonalisedSheetFilesSeedKey appVolatileClusterSettingsCache appAvsQuery = UniWorX{..}
mkFoundation appSettings' appConnPool appSmtpPool appLdapPool appCryptoIDKey appSessionStore appSecretBoxKey appWidgetMemcached appJSONWebKeySet appClusterID appMemcached appUploadCache appVerpSecret appAuthKey appAuthPlugins appPersonalisedSheetFilesSeedKey appVolatileClusterSettingsCache appAvsQuery = UniWorX{..}
tempFoundation = mkFoundation
(error "appSettings' forced in tempFoundation")
(error "connPool forced in tempFoundation")
@ -250,7 +234,6 @@ makeFoundation appSettings''@AppSettings{..} = do
(error "JSONWebKeySet forced in tempFoundation")
(error "ClusterID forced in tempFoundation")
(error "memcached forced in tempFoundation")
(error "memcachedLocal forced in tempFoundation")
(error "MinioConn forced in tempFoundation")
(error "VerpSecret forced in tempFoundation")
(error "AuthKey forced in tempFoundation")
@ -384,12 +367,6 @@ makeFoundation appSettings''@AppSettings{..} = do
$logWarnS "setup" "Clearing memcached"
liftIO $ Memcached.flushAll memcachedConn
return AppMemcached{..}
appMemcachedLocal <- for appMemcachedLocalConf $ \ARCConf{..} -> do
memcachedLocalARC <- initARCHandle arccMaximumGhost arccMaximumWeight
void . Prometheus.register $ arcMetrics ARCMemcachedLocal memcachedLocalARC
memcachedLocalInvalidationQueue <- newTVarIO mempty
memcachedLocalHandleInvalidations <- allocateLinkedAsync . managePostgresqlChannel appDatabaseConf ChannelMemcachedLocalInvalidation $ manageMemcachedLocalInvalidations memcachedLocalARC memcachedLocalInvalidationQueue
return AppMemcachedLocal{..}
appSessionStore <- mkSessionStore appSettings'' sqlPool `customRunSqlPool` sqlPool
@ -428,7 +405,7 @@ makeFoundation appSettings''@AppSettings{..} = do
$logDebugS "Runtime configuration" $ tshowCrop appSettings'
-- TODO: reimplement user db failover
let foundation = mkFoundation appSettings' sqlPool smtpPool ldapPool appCryptoIDKey appSessionStore appSecretBoxKey appWidgetMemcached appJSONWebKeySet appClusterID appMemcached appMemcachedLocal appUploadCache appVerpSecret appAuthKey appAuthPlugins appPersonalisedSheetFilesSeedKey appVolatileClusterSettingsCache appAvsQuery
let foundation = mkFoundation appSettings' sqlPool smtpPool ldapPool appCryptoIDKey appSessionStore appSecretBoxKey appWidgetMemcached appJSONWebKeySet appClusterID appMemcached appUploadCache appVerpSecret appAuthKey appAuthPlugins appPersonalisedSheetFilesSeedKey appVolatileClusterSettingsCache appAvsQuery
-- Return the foundation
$logInfoS "setup" "*** DONE ***"

View File

@ -1,4 +1,4 @@
-- SPDX-FileCopyrightText: 2023-24 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Steffen Jost <s.jost@fraport.de>
-- SPDX-FileCopyrightText: 2023-2024 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Steffen Jost <s.jost@fraport.de>
--
-- SPDX-License-Identifier: AGPL-3.0-or-later

View File

@ -1,4 +1,4 @@
-- 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-FileCopyrightText: 2022-2024 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
@ -213,6 +213,12 @@ data Transaction
, transactionNote :: Maybe Text
, transactionReceived :: UTCTime -- when was the csv file received?
}
| TransactionLmsTerminated
{ transactionQualification :: QualificationId
, transactionLmsIdent :: LmsIdent
, transactionLmsUser :: UserId
, transactionNote :: Maybe Text
}
| TransactionQualificationUserEdit -- Note that a renewal always entails unblocking as well!
{ transactionUser :: UserId -- qualification holder that is updated
, transactionQualificationUser :: QualificationUserId -- not really necessary, maybe remove?
@ -283,7 +289,8 @@ data AdminProblem
, 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
{ adminProblemUser :: UserId -- user who had a supervisor but no longer has, due to supervisor change
, adminProblemEmail :: Maybe Text -- new superior user's email, not found in LDAP
, adminProblemCompany :: CompanyId -- affected company
, adminProblemUserOld :: Maybe UserId -- previous superior
}

View File

@ -1,5 +1,4 @@
-- {-# LANGUAGE BangPatterns #-}
{-# OPTIONS_GHC -Wwarn #-}
{-# OPTIONS_GHC -Wwarn -fno-warn-orphans #-}
-- SPDX-FileCopyrightText: 2024-2025 Sarah Vaupel <sarah.vaupel@uniworx.de>
--
@ -64,8 +63,9 @@ foreign import ccall unsafe "sodium_bin2hex"
bin2hex :: ByteString -> String
bin2hex bs = let tlen = S.length bs * 2 + 1 in
S8.unpack . S8.init . snd . buildUnsafeByteString tlen $ \t ->
constByteStrings [bs] $ \[(pbs, _)] ->
c_sodium_bin2hex t (fromIntegral tlen) pbs (fromIntegral $ S.length bs)
let aux [(pbs, _)] = c_sodium_bin2hex t (fromIntegral tlen) pbs (fromIntegral $ S.length bs)
aux _ = error "Crypto.Saltine.Instances.bin2hex reached an impossible computation path"
in constByteStrings [bs] aux
instance Show Key where
show k = "SecretBox.Key {hashesTo = \"" <> (bin2hex . shorthash nullShKey $ encode k) <> "}\""

View File

@ -117,4 +117,20 @@ instance {-# OVERLAPS #-} FromJSON (E.CryptoID "PrintJob" (CI FilePath)) where
instance {-# OVERLAPS #-} FromJSONKey (E.CryptoID "PrintJob" (CI FilePath)) where
fromJSONKey = FromJSONKeyTextParser $ maybe (fail "Could not parse CryptoPrintJob") return . fromPathPiece
instance {-# OVERLAPS #-} ToMarkup (E.CryptoID "PrintJob" (CI FilePath)) where
toMarkup = toMarkup . toPathPiece
toMarkup = toMarkup . toPathPiece
-- instance PathPiece a => PathPiece [a] where
-- toPathPiece = textBracket '[' ']' . Text.intercalate "," . map toPathPiece
-- fromPathPiece (textUnbracket '[' ']' . Text.strip -> Just t)
-- | null t = Just []
-- | otherwise = mapM fromPathPiece $ Text.split (==',') t
-- fromPathPiece _ = Nothing
instance PathPiece [E.CryptoID "ExamOccurrence" UUID] where -- required for a form field sending multiple ids
fromPathPiece (textUnbracket '[' ']' . Text.strip -> Just t)
| null t = Just []
| otherwise = fromPathMultiPiece $ Text.split (==',') t
fromPathPiece _ = Nothing
toPathPiece = textBracket '[' ']' . Text.intercalate "," . toPathMultiPiece

View File

@ -1,4 +1,4 @@
-- 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-FileCopyrightText: 2022-2025 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
@ -9,9 +9,12 @@ module Database.Esqueleto.Utils
( true, false
, vals, justVal, justValList, toValues
, isJust, alt
, isNumerical, hasLetter
, isInfixOf, hasInfix
, isPrefixOf_, hasPrefix_
, strConcat, substring
, strConcat
, substring, substringRegex
, decodeBase64, encodeEscape, mailContentContains
, (=?.), (?=.)
, (=~.), (~=.)
, (>~.), (<~.)
@ -49,10 +52,11 @@ module Database.Esqueleto.Utils
, unKey
, subSelectCountDistinct
, selectCountRows, selectCountDistinct
, selectMaybe
, str2text, str2text'
, str2citext
, num2text --, text2num
, day, day', dayMaybe, interval, diffDays, diffTimes
, withinPeriod
, exprLift
, explicitUnsafeCoerceSqlExprValue
, psqlVersion_
@ -80,6 +84,7 @@ import Database.Esqueleto.Utils.TH
import qualified Data.Text as Text
import qualified Data.Text.Lazy as Lazy (Text)
import qualified Data.ByteString.Lazy as Lazy (ByteString)
import qualified Data.CaseInsensitive as CI
import Crypto.Hash (Digest, SHA256)
@ -95,7 +100,7 @@ import Data.Monoid (Last(..))
import Utils (commaSeparatedText)
-- import Utils.Set (concatMapSet)
import Model.Types.Mail (MailContent)
{-# ANN any ("HLint: ignore Use any" :: String) #-}
{-# ANN all ("HLint: ignore Use all" :: String) #-}
@ -150,40 +155,51 @@ infixl 4 ?=.
-- | like (=?.) but also succeeds if the right-hand side is NULL. Can often be avoided by moving from where- to join-condition!
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)
-- (=~.) a b = E.isNothing b E.||. (E.just a E.==. b) -- avoid expensive E.||.
(=~.) a b = a E.==. E.coalesceDefault [b] a
infixl 4 ~=.
(~=.) :: 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) -- avoid expensive E.||.
(~=.) a b = b E.==. E.coalesceDefault [a] b
-- | like (>.), but also succeeds if the right-hand side is NULL
-- | 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)
-- (>~.) a b = E.isNothing b E.||. (E.just a E.>. b)
(>~.) a b = a E.>=. E.coalesceDefault [b] a
-- | like (<.), but also succeeds if the right-hand side is NULL
-- | 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)
-- (<~.) a b = E.isNothing b E.||. (E.just a E.<. b)
(<~.) a b = a E.<=. E.coalesceDefault [b] a
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.SqlString s, E.SqlString t) => E.SqlExpr (E.Value s) -> E.SqlExpr (E.Value t) -> 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.SqlString s, E.SqlString t) => E.SqlExpr (E.Value s) -> E.SqlExpr (E.Value t) -> 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.SqlString s, E.SqlString t) => E.SqlExpr (E.Value s) -> E.SqlExpr (E.Value t) -> 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.SqlString s, E.SqlString t) => E.SqlExpr (E.Value s) -> E.SqlExpr (E.Value t) -> E.SqlExpr (E.Value Bool)
(!~*.) = E.unsafeSqlBinOp " !~* "
-- | PostgreSQL regex test if value contains only numbers
isNumerical :: E.SqlString s => E.SqlExpr (E.Value s) -> E.SqlExpr (E.Value Bool)
isNumerical = (~. E.val ("^[0-9]+$"::Text))
-- | PostgreSQL regex test if value contains at least one letter
hasLetter :: E.SqlString s => E.SqlExpr (E.Value s) -> E.SqlExpr (E.Value Bool)
hasLetter = (~*. E.val ("[a-z]"::Text))
-- | Negation of `isNothing` which is missing
isJust :: PersistField typ => E.SqlExpr (E.Value (Maybe typ)) -> E.SqlExpr (E.Value Bool)
@ -245,6 +261,37 @@ substring (E.ERaw _m1 f1) (E.ERaw _m2 f2) (E.ERaw _m3 f3)
, strVals <> fromiVals <> foriVals
)
substringRegex :: ( E.SqlString str, E.SqlString from)
=> E.SqlExpr (E.Value str)
-> E.SqlExpr (E.Value from)
-> E.SqlExpr (E.Value str)
substringRegex (E.ERaw _m1 f1) (E.ERaw _m2 f2)
= E.ERaw E.noMeta $ \_nParens info ->
let (strTLB, strVals) = f1 E.Parens info
(fromiTLB, fromiVals) = f2 E.Parens info
in ( "SUBSTRING" <> E.parens (E.parens strTLB <> " FROM " <> E.parens fromiTLB)
, strVals <> fromiVals
)
-- useful for searching within MailContent in db
decodeBase64 :: E.SqlString str => E.SqlExpr (E.Value str) -> E.SqlExpr (E.Value str)
decodeBase64 = E.unsafeSqlFunction "decode" . (, E.val "base64" :: E.SqlExpr (E.Value Text))
encodeEscape :: E.SqlString str => E.SqlExpr (E.Value str) -> E.SqlExpr (E.Value str)
encodeEscape = E.unsafeSqlFunction "encode" . (, E.val "escape" :: E.SqlExpr (E.Value Text))
mailContentContains :: E.SqlString str => E.SqlExpr (E.Value MailContent) -> E.SqlExpr (E.Value str) -> E.SqlExpr (E.Value Bool)
mailContentContains hay needle = hasNeedle plainText E.||. hasNeedle encodedBase64
where
hayText :: E.SqlExpr (E.Value Text) = E.unsafeSqlCastAs "text" hay
hasNeedle = isInfixOf needle
encodedBase64 = encodeEscape $ decodeBase64 $
substringRegex hayText $ E.val reB64
plainText = substringRegex hayText $ E.val rePlain
reB64 :: Text = ".*\\{\"type\": \"text/plain; charset=utf-8\", \"content\": \\{\"content\": \"(.*?)\", \"encoding\": \"base64\"\\}.*"
rePlain :: Text = ".*\\{\"type\": \"text/plain; charset=utf-8\", \"content\": \"(.*?)\", \"headers\": \\[\\], \"encoding\": \"quoted-printable-text\".*"
explicitUnsafeCoerceSqlExprValue :: forall b a.
Text
-> E.SqlExpr (E.Value a)
@ -281,7 +328,8 @@ subSelectOr q = parens . E.subSelectUnsafe $ flip (E.unsafeSqlAggregateFunction
parens :: E.SqlExpr (E.Value a) -> E.SqlExpr (E.Value a)
parens = E.unsafeSqlFunction ""
-- | Workaround for Esqueleto-Bug not placing parenthesis after NOT, see #155
-- | Workaround for Esqueleto-Bug not placing parenthesis after NOT.
-- This leads to erroneous filters. For examples, see DevOps #1970
not__ :: E.SqlExpr (E.Value Bool) -> E.SqlExpr (E.Value Bool)
not__ = E.not_ . parens
@ -510,10 +558,13 @@ allFilter fltrs needle criterias = F.foldr aux true fltrs
where
aux fltr acc = fltr needle criterias E.&&. acc
-- | Descending order of this field or SqlExpression, but with NULLS at the end.
-- | Ascending order of this field or SqlExpression, but with NULLS at the end.
-- For bool, just use ASC, since false < true < null
ascNullsFirst :: PersistField a => E.SqlExpr (E.Value a) -> E.SqlExpr E.OrderBy
ascNullsFirst = E.orderByExpr " ASC NULLS FIRST"
-- | Descending order of this field or SqlExpression, but with NULLS at the end.
-- Use this if you want the order to be true, false, null
descNullsLast :: PersistField a => E.SqlExpr (E.Value a) -> E.SqlExpr E.OrderBy
descNullsLast = E.orderByExpr " DESC NULLS LAST"
@ -537,6 +588,7 @@ strip = E.unsafeSqlFunction "TRIM"
infix 4 `ciEq`
-- Note that this function is unnecessary if the DB type is citext
ciEq :: E.SqlString s => E.SqlExpr (E.Value s) -> E.SqlExpr (E.Value s) -> E.SqlExpr (E.Value Bool)
ciEq a b = lower a E.==. lower b
@ -739,8 +791,9 @@ selectCountDistinct q = do
_other
-> error "E.countDistinct did not return exactly one result"
selectMaybe :: (E.SqlSelect a r, MonadIO m) => E.SqlQuery a -> E.SqlReadT m (Maybe r)
selectMaybe = fmap listToMaybe . E.select . (<* E.limit 1)
-- DEPRECATED: use Database.Esqueleto.selectOne instead
-- selectMaybe :: (E.SqlSelect a r, MonadIO m) => E.SqlQuery a -> E.SqlReadT m (Maybe r) -- aka selectFirst
-- 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)
@ -749,6 +802,9 @@ str2text = E.unsafeSqlCastAs "text"
str2text' :: E.SqlString a => E.SqlExpr (E.Value (Maybe a)) -> E.SqlExpr (E.Value (Maybe Text))
str2text' = E.unsafeSqlCastAs "text"
str2citext :: E.SqlString a => E.SqlExpr (E.Value a) -> E.SqlExpr (E.Value (CI.CI Text))
str2citext = E.unsafeSqlCastAs "citext"
-- | 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"
@ -767,6 +823,19 @@ day' = E.unsafeSqlCastAs "date"
dayMaybe :: E.SqlExpr (E.Value (Maybe UTCTime)) -> E.SqlExpr (E.Value (Maybe Day))
dayMaybe = E.unsafeSqlCastAs "date"
-- | Given an occurrence with start-time and maybe an end-time, does it overlap with a given day interval?
-- If there is no end-time, then the start-time must be in between.
withinPeriod :: (Day, Day) -> E.SqlExpr (E.Value UTCTime) -> E.SqlExpr (E.Value (Maybe UTCTime)) -> E.SqlExpr (E.Value Bool)
withinPeriod (dbegin, dend) tfrom tto = day tfrom E.<=. E.val dend
E.&&. E.coalesceDefault [dayMaybe tto]
(day tfrom) E.>=. E.val dbegin
-- Alternative variant which SJ expected to be more efficient, if there is an index on the first argument available,
-- but FraportGPT thinks otherwise: "OR conditions may prevent the efficient use of an index. OR conditions can sometimes lead to a full table scan, whereas COALESCE is quite cheap"
-- withinPeriod (dstart, dend) tfrom tto = day tfrom E.<=. E.val dend
-- E.&&. ( day tfrom E.>=. E.val dstart
-- E.||. (isJust tto E.&&. dayMaybe tto E.>=. justVal dstart ))
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 . wrapSqlString . Text.Builder.fromString . iso8601Show
@ -827,4 +896,4 @@ truncateTable :: (MonadIO m, BackendCompatible SqlBackend backend, PersistEntity
=> 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") []
in E.rawExecute ("TRUNCATE TABLE " <> tblName <> " RESTART IDENTITY") []

View File

@ -1,4 +1,4 @@
-- SPDX-FileCopyrightText: 2022 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <vaupel.sarah@campus.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>
-- SPDX-FileCopyrightText: 2022-2024 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <vaupel.sarah@campus.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>,Steffen Jost <s.jost@fraport.de>
--
-- SPDX-License-Identifier: AGPL-3.0-or-later
@ -9,7 +9,7 @@ module Database.Esqueleto.Utils.TH
, sqlInTuple, sqlInTuples
, _unValue
, unValueN, unValueNIs
, sqlIJproj, sqlLOJproj, sqlFOJproj
, sqlIJproj, sqlLOJproj, sqlFOJproj, sqlMIXproj, sqlMIXproj'
) where
import ClassyPrelude
@ -26,6 +26,9 @@ import Data.List (foldr1, foldl)
import Utils.TH
import Control.Lens.Iso (Iso', iso)
{-# ANN module ("HLint: ignore Redundant bracket"::String) #-}
class E.SqlSelect a r => SqlIn a r | a -> r, r -> a where
sqlIn :: a -> [r] -> E.SqlExpr (E.Value Bool)
@ -99,7 +102,7 @@ unValueNIs arity uvIdx = do
-- | Generic projections for InnerJoin-tuples
-- gives I-th element of N-tuple of left-associative InnerJoin-pairs, i.e.
--
-- > $(projN n m) :: (t1 `E.InnerJoin` .. `E.InnerJoin` tn) -> tm@ (for m<=n)
-- > $(sqlIJproj n m) :: (t1 `E.InnerJoin` .. `E.InnerJoin` tn) -> tm@ (for m<=n)
sqlIJproj :: Int -> Int -> ExpQ
sqlIJproj = leftAssociativePairProjection 'E.InnerJoin
@ -108,3 +111,23 @@ sqlLOJproj = leftAssociativePairProjection 'E.LeftOuterJoin
sqlFOJproj :: Int -> Int -> ExpQ
sqlFOJproj = leftAssociativePairProjection 'E.FullOuterJoin
-- | Generic projections for Join-tuple
-- gives i-th element of n-tuple of left-associative join pairs, i.e.
--
-- > $(sqlMIXproj "IR" 3) :: ((t1 `E.InnerJoin` t2) `E.RightOuterJoin` t3) -> t3
sqlMIXproj :: String -> Int -> ExpQ
sqlMIXproj = leftAssociativeProjection . map decodeJoin
where
decodeJoin 'I' = 'E.InnerJoin
decodeJoin 'L' = 'E.LeftOuterJoin
decodeJoin 'R' = 'E.RightOuterJoin
decodeJoin 'F' = 'E.FullOuterJoin
decodeJoin 'O' = 'E.FullOuterJoin
decodeJoin 'X' = 'E.CrossJoin
decodeJoin 'C' = 'E.CrossJoin
decodeJoin c = error $ "Database.Esqueleto.Utils.TH.sqlMIXproj: received unknown SQL join kind \"" ++ c:"\"" -- always raised at compile time, so this is ok
-- Alternative using `reify`; works, but may require `$(return [])` between type definition and call to workaround ghc staging problems
sqlMIXproj' :: Name -> Int -> ExpQ
sqlMIXproj' t i = extractConstructorNames t >>= flip leftAssociativeProjection i

View File

@ -27,7 +27,7 @@ instance Hashable LiteralType
instance Binary LiteralType
instance NFData LiteralType
deriving instance Generic PersistValue
instance Hashable PersistValue

View File

@ -38,7 +38,7 @@ import Handler.Utils.I18n
import Handler.Utils.Routes
import Utils.Course (courseIsVisible)
import Utils.Metrics (observeAuthTagEvaluation, AuthTagEvalOutcome(..))
import qualified Data.Set as Set
import qualified Data.Aeson as JSON
import qualified Data.HashSet as HashSet
@ -95,7 +95,7 @@ instance Exception InvalidAuthTag
type AuthTagsEval m = AuthDNF -> Maybe (AuthId UniWorX) -> Route UniWorX -> Bool -> WriterT (Set AuthTag) m AuthResult
data AccessPredicate
= APPure (Maybe (AuthId UniWorX) -> Route UniWorX -> Bool -> Reader MsgRenderer AuthResult)
| APHandler (Maybe (AuthId UniWorX) -> Route UniWorX -> Bool -> HandlerFor UniWorX AuthResult)
@ -174,7 +174,7 @@ cacheAPDB mExp k mkV cont = APBindDB $ \mAuthId route isWrite -> do
v <- mkV
memcachedBySet mExp k v
either (return . Left) (fmap Right . lift) $ cont mAuthId route isWrite v
-- cacheAP' :: ( Binary k
-- , Typeable v, Binary v
-- )
@ -185,7 +185,7 @@ cacheAPDB mExp k mkV cont = APBindDB $ \mAuthId route isWrite -> do
-- cacheAP' mExp mkKV cont = APBind $ \mAuthId route isWrite -> case mkKV mAuthId route isWrite of
-- Just (k, mkV) -> either (return . Left) (fmap Right) . cont mAuthId route isWrite . Just =<< memcachedBy mExp k mkV
-- Nothing -> either (return . Left) (fmap Right) $ cont mAuthId route isWrite Nothing
cacheAPDB' :: ( Binary k
, Typeable v, Binary v, NFData v
)
@ -313,7 +313,8 @@ isDryRunDB = fmap unIsDryRun . cached . fmap MkIsDryRun $ orM
dnf <- throwLeft $ routeAuthTags currentRoute
let eval :: forall m''. MonadAP m'' => AuthTagsEval m''
eval dnf' mAuthId' route' isWrite' = evalAuthTags 'isDryRun (AuthTagActive $ const True) eval (noTokenAuth dnf') mAuthId' route' isWrite'
-- eval dnf' mAuthId' route' isWrite' = evalAuthTags 'isDryRun (AuthTagActive $ const True) eval (noTokenAuth dnf') mAuthId' route' isWrite'
eval dnf' = evalAuthTags 'isDryRun (AuthTagActive $ const True) eval (noTokenAuth dnf')
in guardAuthResult <=< evalWriterT $ eval dnf mAuthId currentRoute isWrite
return False
@ -368,7 +369,8 @@ validateBearer mAuthId' route' isWrite' token' = $runCachedMemoT $ for4 memo val
noTokenAuth = over _dnfTerms . Set.filter . noneOf (re _nullable . folded) $ (== AuthToken) . plVar
eval :: forall m'. MonadAP m' => AuthTagsEval m'
eval dnf' mAuthId'' route'' isWrite'' = evalAuthTags 'validateBearer (AuthTagActive $ const True) eval (noTokenAuth dnf') mAuthId'' route'' isWrite''
-- eval dnf' mAuthId'' route'' isWrite'' = evalAuthTags 'validateBearer (AuthTagActive $ const True) eval (noTokenAuth dnf') mAuthId'' route'' isWrite''
eval dnf' = evalAuthTags 'validateBearer (AuthTagActive $ const True) eval (noTokenAuth dnf')
bearerAuthority' <- hoist apRunDB $ do
bearerAuthority' <- flip foldMapM bearerAuthority $ \case
@ -538,14 +540,14 @@ tagAccessPredicate AuthAdmin = cacheAPSchoolFunction SchoolAdmin (Just $ Right d
guardMExceptT (isJust adrights) (unauthorizedI MsgUnauthorizedSiteAdmin)
return Authorized
tagAccessPredicate AuthSupervisor = APDB $ \_ _ mAuthId route _ -> case route of
tagAccessPredicate AuthSupervisor = APDB $ \_ _ mAuthId route _ -> case route of
ForProfileR 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
where
r -> $unsupportedAuthPredicate AuthSupervisor r
where
checkSupervisor sup@(mAuthId, cID) = $cachedHereBinary sup . exceptT return return $ do
authId <- maybeExceptT AuthenticationRequired $ return mAuthId
uid <- decrypt cID
@ -553,13 +555,13 @@ tagAccessPredicate AuthSupervisor = APDB $ \_ _ mAuthId route _ -> case route of
guardMExceptT isSupervisor (unauthorizedI MsgUnauthorizedSupervisor)
return Authorized
checkCompanySupervisor sup@(mAuthId, fsh) = $cachedHereBinary sup . exceptT return return $ do
authId <- maybeExceptT AuthenticationRequired $ return mAuthId
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
authId <- maybeExceptT AuthenticationRequired $ return mAuthId
isSupervisor <- lift $ exists [UserSupervisorSupervisor ==. authId]
guardMExceptT isSupervisor (unauthorizedI MsgUnauthorizedAnySupervisor)
return Authorized
@ -692,7 +694,7 @@ tagAccessPredicate AuthLecturer = cacheAPDB' (Just $ Right diffMinute) mkLecture
_ | is _Nothing mAuthId' -> return AuthenticationRequired
CourseR{} -> unauthorizedI MsgUnauthorizedLecturer
EExamR{} -> unauthorizedI MsgUnauthorizedExternalExamLecturer
_other -> unauthorizedI MsgUnauthorizedSchoolLecturer
_other -> unauthorizedI MsgUnauthorizedSchoolLecturer
| otherwise -> Left $ APDB $ \_ _ mAuthId route _ -> case route of
CourseR tid ssh csh _ -> $cachedHereBinary (mAuthId, tid, ssh, csh) . exceptT return return $ do
authId <- maybeExceptT AuthenticationRequired $ return mAuthId
@ -722,7 +724,7 @@ tagAccessPredicate AuthLecturer = cacheAPDB' (Just $ Right diffMinute) mkLecture
return Authorized
where
mkLecturerList _ route _ = case route of
CourseR{} -> cacheLecturerList
CourseR{} -> cacheLecturerList
EExamR{} -> Just
( AuthCacheExternalExamStaffList
, fmap (setOf $ folded . _Value) . E.select . E.from $ return . (E.^. ExternalExamStaffUser)
@ -1199,7 +1201,7 @@ tagAccessPredicate AuthExamRegistered = APDB $ \_ _ mAuthId route _ -> case rout
guardMExceptT hasRegistration $ unauthorizedI MsgUnauthorizedRegisteredExam
return Authorized
CSheetR tid ssh csh shn _ -> exceptT return return $ do
requiredExam' <- $cachedHereBinary (tid, ssh, csh, shn) . lift . E.selectMaybe . E.from $ \(course `E.InnerJoin` sheet) -> do
requiredExam' <- $cachedHereBinary (tid, ssh, csh, shn) . lift . E.selectOne . E.from $ \(course `E.InnerJoin` sheet) -> do
E.on $ sheet E.^. SheetCourse E.==. course E.^. CourseId
E.where_ $ course E.^. CourseTerm E.==. E.val tid
E.&&. course E.^. CourseSchool E.==. E.val ssh
@ -1704,7 +1706,7 @@ evalAccessWith :: (HasCallStack, MonadThrow m, MonadAP m) => [(AuthTag, Bool)] -
evalAccessWith assumptions route isWrite = do
mAuthId <- liftHandler maybeAuthId
evalAccessWithFor assumptions mAuthId route isWrite
evalAccessWithDB :: (HasCallStack, MonadThrow m, MonadAP m, BackendCompatible SqlReadBackend backend) => [(AuthTag, Bool)] -> Route UniWorX -> Bool -> ReaderT backend m AuthResult
evalAccessWithDB = evalAccessWith

View File

@ -1,4 +1,4 @@
-- 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-FileCopyrightText: 2022-2025 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
@ -40,10 +40,9 @@ module Foundation.I18n
, ShortStudyFieldType(..)
, StudyDegreeTermType(..)
, ErrorResponseTitle(..)
, UniWorXMessages(..)
, uniworxMessages
-- , UniWorXMessages(..), uniworxMessages
, unRenderMessage, unRenderMessage', unRenderMessageLenient
, SomeMessages(..)
, SomeMessages(..), pattern SomeMsgs, pattern SpaceMsgs, pattern JoinMsgs
, someMessages
, module Foundation.I18n.TH
) where
@ -62,7 +61,7 @@ import qualified Data.Text as Text
import Utils.Form
import qualified GHC.Exts (IsList(..))
-- import qualified GHC.Exts (IsList(..)) -- for UniWorXMessages
import Yesod.Form.I18n.German
import Yesod.Form.I18n.English
@ -280,16 +279,31 @@ mkMessageAddition ''UniWorX "Avs" "messages/uniworx/categories/avs" "de-de-forma
embedRenderMessage ''UniWorX ''LmsStatus (uncurry ((<>) . (<> "Status")) . Text.splitAt 3)
-- | Flexible variant of `UniWorXMessages` allowing custom separation
data SomeMessages master = SomeMessages Text [SomeMessage master]
newtype SomeMessages master = SomeMessages [SomeMessage master]
deriving newtype (Semigroup, Monoid)
pattern SomeMsgs :: [SomeMessage master] -> SomeMessages master
pattern SomeMsgs msgs = SomeMessages "\n " msgs
pattern SpaceMsgs :: [SomeMessage master] -> SomeMessages master
pattern SpaceMsgs msgs = SomeMessages " " msgs
pattern JoinMsgs :: [SomeMessage master] -> SomeMessages master
pattern JoinMsgs msgs = SomeMessages "" msgs
-- Not yet needed:
-- instance Semigroup (SomeMessage master) where
-- (SomeMessages s1 t1) <> (SomeMessages _s2 t2) = SomeMessages s1 $ t1 ++ t2
-- instance Monoid (SomeMessage master) where
-- mempty = SomeMessages mempty mempty
instance master ~ master' => RenderMessage master (SomeMessages master') where
renderMessage a b (SomeMessages msgs) = Text.intercalate "\n " $ renderMessage a b <$> msgs
renderMessage a b (SomeMessages sep msgs) = Text.intercalate sep $ 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
someMessages msgs = SomeMessages "\n " $ SomeMessage <$> msgs
instance RenderMessage UniWorX (Maybe LmsStatus) where -- useful for Filter with optionsFinite
@ -535,22 +549,24 @@ instance HasResolution a => ToMessage (Fixed a) where
newtype ErrorResponseTitle = ErrorResponseTitle ErrorResponse
embedRenderMessageVariant ''UniWorX ''ErrorResponseTitle ("ErrorResponseTitle" <>)
-- -- A list of messages is a message by itself. Uses blank for separation.
-- -- Deprecated for now; replaced by the more flexibles SomeMessages. Easy to reinstate.
-- --
-- newtype UniWorXMessages = UniWorXMessages [SomeMessage UniWorX]
-- deriving stock (Generic)
-- deriving newtype (Semigroup, Monoid)
newtype UniWorXMessages = UniWorXMessages [SomeMessage UniWorX]
deriving stock (Generic)
deriving newtype (Semigroup, Monoid)
-- instance IsList UniWorXMessages where
-- type Item UniWorXMessages = SomeMessage UniWorX
-- fromList = UniWorXMessages
-- toList (UniWorXMessages msgs) = msgs
instance IsList UniWorXMessages where
type Item UniWorXMessages = SomeMessage UniWorX
fromList = UniWorXMessages
toList (UniWorXMessages msgs) = msgs
-- instance RenderMessage UniWorX UniWorXMessages where
-- renderMessage foundation ls (UniWorXMessages msgs) =
-- Text.unwords $ map (renderMessage foundation ls) msgs -- Text.unwords uses blank for separation
instance RenderMessage UniWorX UniWorXMessages where
renderMessage foundation ls (UniWorXMessages msgs) =
Text.unwords $ map (renderMessage foundation ls) msgs
uniworxMessages :: [UniWorXMessage] -> UniWorXMessages
uniworxMessages = UniWorXMessages . map SomeMessage
-- uniworxMessages :: [UniWorXMessage] -> UniWorXMessages
-- uniworxMessages = UniWorXMessages . map SomeMessage
-- This instance is required to use forms. You can modify renderMessage to

View File

@ -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-2025 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>,Steffen Jost <s.jost@fraport.de>
--
-- SPDX-License-Identifier: AGPL-3.0-or-later
@ -29,10 +29,12 @@ import Foundation.Routes
import Foundation.I18n
import Foundation.Authorization
import Utils.Company (areThereInsaneCompanySupervisions)
import Utils.Sheet
import Handler.Utils.DateTime
import Handler.Utils.Memcached
import Handler.Utils.ExamOffice.Course
import Utils.Sheet
import qualified Data.Set as Set
import qualified Data.Map as Map
@ -105,6 +107,7 @@ breadcrumb (UserPasswordR cID) = useRunDB $ do
-> i18nCrumb MsgMenuUserPassword . Just $ AdminUserR cID
| otherwise
-> i18nCrumb MsgMenuUserPassword $ Just ProfileR
breadcrumb (UserRecipientsR cID) = i18nCrumb MsgBreadcrumbUserRecipients . Just $ AdminUserR cID
breadcrumb AdminNewFunctionaryInviteR = i18nCrumb MsgMenuLecturerInvite $ Just UsersR
breadcrumb AdminFunctionaryInviteR = i18nCrumb MsgBreadcrumbFunctionaryInvite Nothing
@ -129,6 +132,7 @@ breadcrumb ConfigInterfacesR = i18nCrumb MsgConfigInterfacesHeading $ Just
breadcrumb FirmAllR = i18nCrumb MsgMenuFirms Nothing
breadcrumb FirmsCommR{} = i18nCrumb MsgMenuFirmsComm $ Just FirmAllR
breadcrumb FirmsSupervisionR= i18nCrumb MsgMenuFirmsSupervision $ Just FirmAllR
breadcrumb FirmUsersR{} = i18nCrumb MsgMenuFirmUsers $ Just FirmAllR
breadcrumb (FirmSupersR fsh)= i18nCrumb MsgMenuFirmSupervisors $ Just $ FirmUsersR fsh
breadcrumb (FirmCommR fsh)= i18nCrumb MsgMenuFirmsComm $ Just $ FirmUsersR fsh
@ -146,13 +150,19 @@ breadcrumb PrintAckR{} = i18nCrumb MsgMenuPrintSend $ Just PrintCenter
breadcrumb PrintAckDirectR{}= i18nCrumb MsgMenuPrintAck $ Just PrintCenterR
breadcrumb PrintLogR = i18nCrumb MsgMenuPrintLog $ Just PrintCenterR
breadcrumb SchoolListR = i18nCrumb MsgMenuSchoolList $ Just AdminR
breadcrumb (SchoolR ssh sRoute) = case sRoute of
SchoolEditR -> useRunDB . maybeT (i18nCrumb MsgBreadcrumbSchool $ Just SchoolListR) $ do
breadcrumb SchoolListR = i18nCrumb MsgMenuSchoolList $ Just AdminR
breadcrumb (SchoolR ssh SchoolEditR) =
useRunDB . maybeT (i18nCrumb MsgBreadcrumbSchool $ Just SchoolListR) $ do
School{..} <- MaybeT $ get ssh
isAdmin <- lift $ hasReadAccessTo SchoolListR
return (CI.original schoolName, bool Nothing (Just SchoolListR) isAdmin)
breadcrumb SchoolNewR = i18nCrumb MsgMenuSchoolNew $ Just SchoolListR
breadcrumb (SchoolR ssh (SchoolDayR d)) = do
dt <- formatTime SelFormatDate d
mr <- getMessageRender
return (mr $ MsgMenuSchoolDay ssh dt, Just SchoolListR)
breadcrumb (SchoolR ssh (SchoolDayCheckR d))
= i18nCrumb MsgMenuSchoolDayCheck $ Just (SchoolR ssh (SchoolDayR d))
breadcrumb SchoolNewR = i18nCrumb MsgMenuSchoolNew $ Just SchoolListR
breadcrumb (ExamOfficeR EOExamsR) = i18nCrumb MsgMenuExamOfficeExams Nothing
breadcrumb (ExamOfficeR EOFieldsR) = i18nCrumb MsgMenuExamOfficeFields . Just $ ExamOfficeR EOExamsR
@ -181,12 +191,14 @@ breadcrumb InstanceR = i18nCrumb MsgMenuInstance Nothing
breadcrumb StatusR = i18nCrumb MsgMenuHealth Nothing -- never displayed
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
guardM . lift . existsBy . UniqueSchoolShorthand $ unSchoolKey ssh
return (CI.original $ unSchoolKey ssh, Just QualificationAllR)
breadcrumb (QualificationNewR ssh ) = i18nCrumb MsgMenuQualificationNew $ Just $ QualificationSchoolR ssh
breadcrumb (QualificationR ssh qsh) =useRunDB . maybeT (i18nCrumb MsgBreadcrumbCourse . Just $ QualificationSchoolR ssh) $ do
guardM . lift . existsBy $ SchoolQualificationShort ssh qsh
return (CI.original qsh, Just $ QualificationSchoolR ssh)
breadcrumb (QualificationEditR ssh qsh) = i18nCrumb MsgMenuQualificationEdit $ Just $ QualificationR ssh qsh
breadcrumb QualificationSAPDirectR = i18nCrumb MsgMenuSap $ Just QualificationAllR -- never displayed
breadcrumb LmsAllR = i18nCrumb MsgMenuLms Nothing
@ -196,10 +208,10 @@ breadcrumb (LmsSchoolR ssh ) = useRunDB . maybeT (i18nCrumb MsgBrea
breadcrumb (LmsR ssh qsh) = useRunDB . maybeT (i18nCrumb MsgBreadcrumbCourse . Just $ LmsSchoolR ssh) $ do
guardM . lift . existsBy $ SchoolQualificationShort ssh qsh
return (CI.original qsh, Just $ LmsSchoolR ssh)
breadcrumb (LmsEditR ssh qsh) = i18nCrumb MsgMenuLmsEdit $ Just $ LmsR ssh qsh
-- v2
breadcrumb (LmsLearnersR ssh qsh) = i18nCrumb MsgMenuLmsLearners $ Just $ LmsR ssh qsh
breadcrumb (LmsLearnersDirectR ssh qsh) = i18nCrumb MsgMenuLmsLearners $ Just $ LmsLearnersR ssh qsh -- never displayed, TypedContent
breadcrumb (LmsOrphansR ssh qsh) = i18nCrumb MsgLmsOrphans $ Just $ LmsLearnersR ssh qsh
breadcrumb (LmsReportR ssh qsh) = i18nCrumb MsgMenuLmsReport $ Just $ LmsR ssh qsh
breadcrumb (LmsReportUploadR ssh qsh) = i18nCrumb MsgMenuLmsUpload $ Just $ LmsReportR ssh qsh
breadcrumb (LmsReportDirectR ssh qsh) = i18nCrumb MsgMenuLmsUpload $ Just $ LmsReportR ssh qsh -- never displayed
@ -305,6 +317,7 @@ breadcrumb (CourseR tid ssh csh (TutorialR tutn sRoute)) = case sRoute of
guardM . lift . hasReadAccessTo $ CTutorialR tid ssh csh tutn TUsersR
return (CI.original tutn, Just $ CourseR tid ssh csh CTutorialListR)
TAddUserR -> i18nCrumb MsgMenuTutorialAddMembers . Just $ CTutorialR tid ssh csh tutn TUsersR
TExamR exn -> i18nCrumb (MsgMenuTutorialExam exn) . 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
TCommR -> i18nCrumb MsgMenuTutorialComm . Just $ CTutorialR tid ssh csh tutn TUsersR
@ -935,19 +948,37 @@ pageActions :: ( MonadHandler m
, MonadUnliftIO m
)
=> Route UniWorX -> m [Nav]
pageActions NewsR = return
[ NavPageActionPrimary
{ navLink = NavLink
{ navLabel = MsgMenuOpenCourses
, navRoute = (CourseListR, [("courses-openregistration", toPathPiece True)])
, navAccess' = NavAccessTrue
, navType = NavTypeLink { navModal = False }
, navQuick' = mempty
, navForceActive = False
pageActions NewsR = do
now <- liftIO getCurrentTime
let nowaday = utctDay now
nd <- formatTime SelFormatDate now
schools <- useRunDB $ selectList [] [Asc SchoolShorthand]
return $
( NavPageActionPrimary
{ navLink = NavLink
{ navLabel = MsgMenuOpenCourses
, navRoute = (CourseListR, [("courses-openregistration", toPathPiece True)])
, navAccess' = NavAccessTrue
, navType = NavTypeLink { navModal = False }
, navQuick' = mempty
, navForceActive = False
}
, navChildren = []
}
, navChildren = []
}
]
) :
[ NavPageActionPrimary
{ navLink = NavLink
{ navLabel = MsgMenuSchoolDay ssh nd
, navRoute = SchoolR ssh $ SchoolDayR nowaday
, navAccess' = NavAccessTrue
, navType = NavTypeLink { navModal = False }
, navQuick' = mempty
, navForceActive = False
}
, navChildren = []
}
| sch <- schools, let ssh = sch ^. _entityKey
]
pageActions (CourseR tid ssh csh CShowR) = do
materialListSecondary <- pageQuickActions NavQuickViewPageActionSecondary $ CourseR tid ssh csh MaterialListR
tutorialListSecondary <- pageQuickActions NavQuickViewPageActionSecondary $ CourseR tid ssh csh CTutorialListR
@ -1177,6 +1208,18 @@ pageActions SchoolListR = return
, navChildren = []
}
]
pageActions (SchoolR ssh (SchoolDayR nd)) = return $
( NavPageActionPrimary
{ navLink = defNavLinkModal MsgMenuSchoolDayCheck $ SchoolR ssh $ SchoolDayCheckR nd
, navChildren = []
}
) :
[ NavPageActionPrimary
{ navLink = defNavLink msg $ SchoolR ssh (SchoolDayR $ addDays n nd)
, navChildren = []
}
| (msg, n) <- [(MsgWeekPrev, -7), (MsgDayPrev, -1), (MsgDayNext, 1), (MsgWeekNext, 7)]
]
pageActions UsersR = return
[ NavPageActionPrimary
{ navLink = NavLink
@ -1763,6 +1806,12 @@ pageActions (CTutorialR tid ssh csh tutn TUsersR) = do
}
}
]
pageActions (CTutorialR tid ssh csh _tutn (TExamR ename)) = return
[ NavPageActionPrimary
{ navLink = defNavLink MsgMenuExamEditComplete $ CourseR tid ssh csh $ ExamR ename EEditR
, navChildren = []
}
]
pageActions (CourseR tid ssh csh CExamListR) = return
[ NavPageActionPrimary
{ navLink = NavLink
@ -1957,7 +2006,7 @@ pageActions (CSheetR tid ssh csh shn SShowR) = do
{ navLabel = MsgMenuSheetPersonalisedFiles
, navRoute = CSheetR tid ssh csh shn SPersonalFilesR
, navAccess' = NavAccessDB $
let onlyPersonalised = fmap (maybe False $ not . E.unValue) . E.selectMaybe . E.from $ \(sheet `E.InnerJoin` course) -> do
let onlyPersonalised = fmap (maybe False $ not . E.unValue) . E.selectOne . E.from $ \(sheet `E.InnerJoin` course) -> do
E.on $ sheet E.^. SheetCourse E.==. course E.^. CourseId
E.where_$ sheet E.^. SheetName E.==. E.val shn
E.&&. course E.^. CourseTerm E.==. E.val tid
@ -2378,6 +2427,20 @@ pageActions ParticipantsListR = return
, navChildren = []
}
]
pageActions QualificationAllR = do
schools <- useRunDB $ selectList [] [Asc SchoolShorthand] -- selectKeysList here mysteriously leads to runtime error: InternalError "selectKeysImpl:" School: keyFromValues failed
return [ NavPageActionSecondary { navLink = defNavLink (SomeMessage $ unSchoolKey sid) (QualificationSchoolR sid) } | Entity{entityKey=sid} <- schools ]
pageActions (QualificationSchoolR sid) = return
[ NavPageActionSecondary {
navLink = defNavLink MsgMenuQualificationNew $ QualificationNewR sid
}
]
pageActions (QualificationR sid qsh) = return
[ NavPageActionSecondary {
navLink = defNavLink MsgMenuQualificationEdit $ QualificationEditR sid qsh
}
]
pageActions (LmsR sid qsh) = return
[ NavPageActionPrimary
{ navLink = defNavLink MsgMenuLmsLearners $ LmsLearnersR sid qsh
@ -2393,12 +2456,18 @@ pageActions (LmsR sid qsh) = return
]
}
, NavPageActionSecondary {
navLink = defNavLink MsgMenuLmsEdit $ LmsEditR sid qsh
navLink = defNavLink MsgMenuQualificationEdit $ QualificationEditR sid qsh
}
-- , NavPageActionSecondary {
-- navLink = defNavLink MsgMenuLmsFake $ LmsFakeR sid qsh
-- }
]
pageActions (LmsLearnersR sid qsh) = return
[ NavPageActionPrimary
{ navLink = defNavLink MsgLmsOrphans $ LmsOrphansR sid qsh
, navChildren = []
}
]
pageActions ApiDocsR = return
[ NavPageActionPrimary
{ navLink = NavLink
@ -2412,6 +2481,12 @@ pageActions ApiDocsR = return
, navChildren = []
}
]
pageActions FirmAllR = do
let navLink = defNavLink MsgMenuFirmsSupervision FirmsSupervisionR
navChildren = []
(thereAre,_) <- liftHandler areThereInsaneCompanySupervisions
return [ NavPageActionPrimary{..} | thereAre ]
pageActions (FirmUsersR fsh) = return
[ NavPageActionPrimary
{ navLink = defNavLink MsgTableCompanyNrSupers $ FirmSupersR fsh
@ -2559,7 +2634,7 @@ submissionList tid csh shn uid = withReaderT (projectBackend @SqlReadBackend) .
E.on $ sheet E.^. SheetCourse E.==. course E.^. CourseId
E.where_ $ submissionUser E.^. SubmissionUserUser E.==. E.val uid
E.&&. sheet E.^. SheetName E.==. E.val shn
E.&&. sheet E.^. SheetName E.==. E.val shn
E.&&. course E.^. CourseShorthand E.==. E.val csh
E.&&. course E.^. CourseTerm E.==. E.val tid

View File

@ -258,7 +258,7 @@ siteLayout' overrideHeading widget = do
forM_ authTagPivots $
\authTag -> addMessageWidget Info $ msgModal [whamlet|_{MsgUnauthorizedDisabledTag authTag}|] (Left $ SomeRoute (AuthPredsR, catMaybes [(toPathPiece GetReferer, ) . toPathPiece <$> mcurrentRoute]))
getMessages
storedReasonAndToggleRoute <- case mcurrentRoute of
(Just (CourseR tid ssh csh _)) -> (, Just . SomeRoute $ CourseR tid ssh csh CFavouriteR) <$> storedFavouriteReason tid ssh csh muid
_otherwise -> pure (Nothing, Nothing)
@ -270,8 +270,8 @@ siteLayout' overrideHeading widget = do
, nav'
, contentHeadline
, mmsgs
, maybe userDefaultMaxFavouriteTerms userMaxFavouriteTerms $ view _2 <$> muid
, maybe userDefaultTheme userTheme $ view _2 <$> muid
, maybe userDefaultMaxFavouriteTerms (userMaxFavouriteTerms . view _2) muid
, maybe userDefaultTheme (userTheme . view _2) muid
, storedReasonAndToggleRoute
)
@ -303,7 +303,7 @@ siteLayout' overrideHeading widget = do
| otherwise = Set.drop (Set.size ts - n) ts
currentTerms = toTermKeySet $ filter (views (_2 . _Value) . maybe True $ is _FavouriteCurrent) favourites'
toTermKeySet = setOf $ folded . _1 . _2 . to unTermKey
favourites <- fmap catMaybes . forM favourites' $ \(c@(_, tid, ssh, csh), E.Value mFavourite, courseVisible, mayView, mayEdit)
-> let courseRoute = CourseR tid ssh csh CShowR
favouriteReason = fromMaybe FavouriteCurrent mFavourite
@ -512,7 +512,7 @@ siteLayout' overrideHeading widget = do
$(widgetFile "default-layout")
withUrlRenderer $(hamletFile "templates/default-layout-wrapper.hamlet")
getSystemMessageState :: (MonadHandler m, HandlerSite m ~ UniWorX, BearerAuthSite UniWorX) => SystemMessageId -> m UserSystemMessageState
getSystemMessageState smId = liftHandler $ do
muid <- maybeAuthId
@ -598,7 +598,7 @@ applySystemMessages = maybeT_ . catchMPlus (Proxy @CryptoIDError) $ do
-- FIXME: Move headings into their respective handlers
-- | Method for specifying page heading for handlers that call defaultLayout
--
-- All handlers whose code is under our control should use

View File

@ -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>
-- SPDX-FileCopyrightText: 2022-26 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
@ -11,11 +11,11 @@ module Foundation.Type
, _SessionStorageMemcachedSql, _SessionStorageAcid
, AppMemcached(..)
, _memcachedKey, _memcachedConn
, AppMemcachedLocal(..)
, _memcachedLocalARC
, 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
, DB, DBRead, Form, MsgRenderer, MailM, DBFile
, DB
, DBRead, DBRead', DBReadUq, DBReadUq'
, Form, MsgRenderer, MailM, DBFile
) where
import Import.NoFoundation
@ -32,15 +32,10 @@ import qualified Jose.Jwk as Jose
import qualified Database.Memcached.Binary.IO as Memcached
import Network.Minio (MinioConn)
import Data.IntervalMap.Strict (IntervalMap)
import qualified Utils.Pool as Custom
import Utils.Metrics (DBConnUseState)
import qualified Data.ByteString.Lazy as Lazy
import Data.Time.Clock.POSIX (POSIXTime)
import GHC.Fingerprint (Fingerprint)
import Handler.Sheet.PersonalisedFiles.Types (PersonalisedSheetFilesSeedKey)
import Utils.Avs (AvsQuery())
@ -62,13 +57,6 @@ data AppMemcached = AppMemcached
makeLenses_ ''AppMemcached
data AppMemcachedLocal = AppMemcachedLocal
{ memcachedLocalARC :: ARCHandle (Fingerprint, Lazy.ByteString) Int (NFDynamic, Maybe POSIXTime)
, memcachedLocalHandleInvalidations :: Async ()
, memcachedLocalInvalidationQueue :: TVar (Seq (Fingerprint, Lazy.ByteString))
} deriving (Generic)
makeLenses_ ''AppMemcachedLocal
-- | The foundation datatype for your application. This can be a good place to
-- keep settings and values requiring initialization before your application
@ -93,14 +81,10 @@ data UniWorX = UniWorX
, appJSONWebKeySet :: Jose.JwkSet
, appHealthReport :: TVar (Set (UTCTime, HealthReport))
, appMemcached :: Maybe AppMemcached
, appMemcachedLocal :: Maybe AppMemcachedLocal
, appUploadCache :: Maybe MinioConn
, appVerpSecret :: VerpSecret
, appAuthKey :: Auth.Key
, appAuthPlugins :: [AuthPlugin UniWorX]
, appFileSourceARC :: Maybe (ARCHandle (FileContentChunkReference, (Int, Int)) Int ByteString)
, appFileSourcePrewarm :: Maybe (LRUHandle (FileContentChunkReference, (Int, Int)) UTCTime Int ByteString)
, appFileInjectInhibit :: TVar (IntervalMap UTCTime (Set FileContentReference))
, appPersonalisedSheetFilesSeedKey :: PersonalisedSheetFilesSeedKey
, appVolatileClusterSettingsCache :: TVar VolatileClusterSettingsCache
, appStartTime :: UTCTime -- for Status Page
@ -124,9 +108,17 @@ instance HasCookieSettings RegisteredCookie UniWorX where
instance (MonadHandler m, HandlerSite m ~ UniWorX) => ReadLogSettings m where
readLogSettings = liftIO . readTVarIO =<< getsYesod (view _appLogSettings)
type DB = YesodDB UniWorX
type DBRead = ReaderT SqlReadBackend (HandlerFor UniWorX)
-- ~ ReaderT SqlBackend (HandlerFor UniWorX)
-- type DBRead = ReaderT SqlReadBackend (HandlerFor UniWorX) -- old, was too unflexible. Try DBRead first, then add suffixes ' or Uq until it types ;)
type DBRead a = forall backend . (PersistQueryRead backend, BackendCompatible SqlBackend backend)
=> ReaderT backend (HandlerFor UniWorX) a
type DBRead' a = forall backend . (PersistQueryRead backend, BackendCompatible SqlBackend backend, BaseBackend backend ~ SqlBackend) -- ought to be redundant, but somehow isn´t. Using this everywhere give redundant constraint warnings, also undesirable
=> ReaderT backend (HandlerFor UniWorX) a
type DBReadUq a = forall backend . (PersistQueryRead backend, BackendCompatible SqlBackend backend, PersistUniqueRead backend) -- adding this to DBRead would yield some unnecessary constraint warnings
=> ReaderT backend (HandlerFor UniWorX) a
type DBReadUq' a = forall backend . (PersistQueryRead backend, BackendCompatible SqlBackend backend, BaseBackend backend ~ SqlBackend, PersistUniqueRead backend)
=> ReaderT backend (HandlerFor UniWorX) a
type Form x = Html -> MForm (HandlerFor UniWorX) (FormResult x, WidgetFor UniWorX ())
type MsgRenderer = MsgRendererS UniWorX -- see Utils
type MailM a = MailT (HandlerFor UniWorX) a

View File

@ -1,4 +1,4 @@
-- SPDX-FileCopyrightText: 2022-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>,Gregor Kleen <gregor.kleen@ifi.lmu.de>, Steffen Jost <jost@tcs.ifi.lmu.de>,Steffen Jost <s.jost@fraport.de>
-- SPDX-FileCopyrightText: 2022-202025 Sarah Vaupel <sarah.vaupel@uniworx.de>,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
@ -25,6 +25,7 @@ import qualified Database.Esqueleto.Legacy as EL (on) -- needed for dbTable
import qualified Database.Esqueleto.Utils as E
import Jobs
import Utils.Company (areThereInsaneCompanySupervisions)
import Handler.Utils
import Handler.Utils.Avs
import Handler.Utils.Users
@ -83,13 +84,18 @@ handleAdminProblems mbProblemTable = do
flagNonZero n | n <= 0 = flagError True
| otherwise = messageTooltip =<< handlerToWidget (messageI Error (MsgProblemsDriverSynch n))
(usersAreReachable, driversHaveAvsIds, rDriversHaveFs, noStalePrintJobs, noBadAPCids, (interfaceOks, interfaceTable)) <- runDB $ (,,,,,)
<$> areAllUsersReachable
<*> allDriversHaveAvsId now
showDiffTime t =
let d = diffUTCTime now t
in guardMonoid (d > secondsToNominalDiffTime 30) [whamlet|<small>_{MsgProblemLastCheckTime (formatDiffDays d)}|]
(usersAreReachable, aurTime) <- areAllUsersReachable -- cached
(not -> thereAreInsaneFirmSupervisions, ifsTime) <- areThereInsaneCompanySupervisions -- cached
(driversHaveAvsIds, rDriversHaveFs, not -> noStalePrintJobs, not -> noBadAPCids) <- runDBRead $ (,,,)
<$> allDriversHaveAvsId now
<*> allRDriversHaveFs now
<*> (not <$> exists [PrintJobAcknowledged ==. Nothing, PrintJobCreated <. cutOffOldTime])
<*> (not <$> exists [PrintAcknowledgeProcessed ==. False])
<*> mkInterfaceLogTable mempty
<*> exists [PrintJobAcknowledged ==. Nothing, PrintJobCreated <. cutOffOldTime]
<*> exists [PrintAcknowledgeProcessed ==. False]
(interfaceOks, interfaceTable) <- runDB $ mkInterfaceLogTable mempty
let interfacesBadNr = length $ filter (not . snd) interfaceOks
-- interfacesOk = all snd interfaceOks
@ -145,7 +151,7 @@ postAdminProblemsR = do
getProblemUnreachableR, postProblemUnreachableR :: Handler Html
getProblemUnreachableR = postProblemUnreachableR
postProblemUnreachableR = do
unreachables <- runDB retrieveUnreachableUsers
unreachables <- runDBRead retrieveUnreachableUsers
-- the following form is a nearly identicaly copy from Handler.Users:
((noreachUsersRes, noreachUsersWgt'), noreachUsersEnctype) <- runFormPost . identifyForm FIDUnreachableUsersAction $ buttonForm
@ -219,9 +225,13 @@ mkUnreachableUsersTable = do
dbtColonnade =
-}
areAllUsersReachable :: DB Bool
-- areAllUsersReachable = E.selectNotExists retrieveUnreachableUsers' -- works and would be more efficient, but we cannot check proper email validity within DB alone
areAllUsersReachable = null <$> retrieveUnreachableUsers
areAllUsersReachable :: Handler (Bool, UTCTime)
areAllUsersReachable = $(memcachedByHere) (Just . Right $ 22 * diffHour) [st|isane-users-reachable|] $ do
now <- liftIO getCurrentTime
res <- runDBRead retrieveUnreachableUsers
-- res <- E.selectNotExists retrieveUnreachableUsers' -- works and would be more efficient, but we cannot check proper email validity within DB alone
$logInfoS "sanity" [st|Are there insane company supervisions: #{tshow res}|]
return (null res,now)
-- retrieveUnreachableUsers' :: E.SqlQuery (E.SqlExpr (Entity User))
-- retrieveUnreachableUsers' = do
@ -232,7 +242,7 @@ areAllUsersReachable = null <$> retrieveUnreachableUsers
-- E.&&. E.not_ ((user E.^. UserEmail) `E.like` E.val "%@%.%")
-- return user
retrieveUnreachableUsers :: DB [Entity User]
retrieveUnreachableUsers :: DBReadUq' [Entity User]
retrieveUnreachableUsers = do
emailOnlyUsers <- E.select $ do
user <- E.from $ E.table @User
@ -252,7 +262,7 @@ retrieveUnreachableUsers = do
hasInvalidEmail = fmap isNothing . getUserEmail
allDriversHaveAvsId :: UTCTime -> DB Bool
allDriversHaveAvsId :: UTCTime -> DBReadUq Bool
-- allDriversHaveAvsId = fmap isNothing . E.selectOne . retrieveDriversWithoutAvsId
allDriversHaveAvsId = E.selectNotExists . retrieveDriversWithoutAvsId
@ -299,7 +309,7 @@ retrieveDriversWithoutAvsId now = do
return usr
allRDriversHaveFs :: UTCTime -> DB Bool
allRDriversHaveFs :: UTCTime -> DBReadUq Bool
-- allRDriversHaveFs = fmap isNothing . E.selectOne . retrieveDriversRWithoutF
allRDriversHaveFs = E.selectNotExists . retrieveDriversRWithoutF
@ -368,22 +378,22 @@ mkProblemLogTable = do
, 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)
dbtSorting = Map.fromList
[ ("time" , SortColumn $ queryProblem >>> (E.^. ProblemLogTime))
, ("info" , SortColumn $ queryProblem >>> (E.^. ProblemLogInfo))
-- , ("firm" , SortColumn ((E.->>. "company" ).(queryProblem >>> (E.^. ProblemLogInfo))))
, ("firm" , SortColumn $ \r -> queryProblem r E.^. ProblemLogInfo E.->>. "company")
, ("user" , sortUserNameBareM queryUser)
, ("solved", SortColumn $ queryProblem >>> (E.^. ProblemLogSolved))
, ("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
dbtFilter = Map.fromList
[ ("user" , FilterColumn . E.mkContainsFilterWithCommaPlus Just $ views (to queryUser) (E.?. UserDisplayName))
, ("solver" , FilterColumn . E.mkContainsFilterWithCommaPlus Just $ views (to querySolver) (E.?. UserDisplayName))
, ("company" , FilterColumn . E.mkContainsFilter $ views (to queryProblem) ((E.->>. "company").(E.^. ProblemLogInfo)))
, ("solved" , FilterColumn . E.mkExactFilterLast $ views (to queryProblem) (E.isJust . (E.^. ProblemLogSolved)))
-- , ("problem" , FilterColumn . E.mkContainsFilter $ views (to queryProblem) ((E.->>. "problem").(E.^. ProblemLogInfo))) -- not stored in plaintext!
, ("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
@ -398,9 +408,9 @@ mkProblemLogTable = do
, 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
acts = Map.fromList
[ (ProblemTableMarkSolved , pure ProblemTableMarkSolvedData)
, (ProblemTableMarkUnsolved , pure ProblemTableMarkUnsolvedData)
]
dbtParams = DBParamsForm
{ dbParamsFormMethod = POST

View File

@ -1,4 +1,4 @@
-- SPDX-FileCopyrightText: 2022 Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Steffen Jost <jost@tcs.ifi.lmu.de>
-- SPDX-FileCopyrightText: 2022-2024 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
@ -38,10 +38,6 @@ import qualified Database.Esqueleto.Utils as E
-- import Database.Esqueleto.Utils.TH
-- avoids repetition of local definitions
single :: (k,a) -> Map k a
single = uncurry Map.singleton
exceptionWgt :: SomeException -> Widget
exceptionWgt (SomeException e) = [whamlet|<h2>Error:</h2> #{tshow e}|]
@ -627,7 +623,7 @@ instance HasUser LicenceTableData where
mkLicenceTable :: AvsPersonIdMapPersonCard -> Set AvsPersonId -> Text -> AvsLicence -> Set AvsPersonId -> DB (FormResult (LicenceTableActionData, Set AvsPersonId), Widget)
mkLicenceTable apidStatus rsChanged dbtIdent aLic apids = do
(currentRoute, usrHasAvsRerr) <- liftHandler $ (,)
<$> (fromMaybe (error "mkLicenceTable called from 404-handler") <$> liftHandler getCurrentRoute)
<$> fmap (fromMaybe $ error "mkLicenceTable called from 404-handler") (liftHandler getCurrentRoute)
<*> (messageTooltip <$> messageI Error MsgProblemAvsUsrHadR)
avsQualifications <- selectList [QualificationAvsLicence !=. Nothing] [Asc QualificationName]
now <- liftIO getCurrentTime
@ -692,23 +688,23 @@ mkLicenceTable apidStatus rsChanged dbtIdent aLic apids = do
) $ \(preview $ resultQualUser . _entityVal . _qualificationUserScheduleRenewal -> b) -> cellMaybe (flip ifIconCell IconNoNotification . not) b
, sortable Nothing (i18nCell MsgTableAvsActiveCards) $ \(view $ resultUserAvs . _userAvsPersonId -> apid) -> foldMap avsPersonCardCell $ Map.lookup apid apidStatus
]
dbtSorting = mconcat
[ single $ sortUserNameLink queryUser
, single ("avspersonno" , SortColumn $ queryUserAvs >>> (E.^. UserAvsNoPerson))
, single ("qualification" , SortColumn $ queryQualification >>> (E.?. QualificationShorthand))
, single $ sortUserCompany queryUser
, single ("valid-until" , SortColumn $ queryQualUser >>> (E.?. QualificationUserValidUntil))
, single ("last-refresh" , SortColumn $ queryQualUser >>> (E.?. QualificationUserLastRefresh))
, single ("first-held" , SortColumn $ queryQualUser >>> (E.?. QualificationUserFirstHeld))
, single ("blocked" , SortColumnNeverNull $ queryQualBlock >>> (E.?. QualificationUserBlockFrom))
, single ("schedule-renew", SortColumnNullsInv $ queryQualUser >>> (E.?. QualificationUserScheduleRenewal))
-- , single ("validity" , SortColumn $ queryQualUser >>> (E.?. QualificationUserValidUntil))
dbtSorting = Map.fromList
[ sortUserNameLink queryUser
, ("avspersonno" , SortColumn $ queryUserAvs >>> (E.^. UserAvsNoPerson))
, ("qualification" , SortColumn $ queryQualification >>> (E.?. QualificationShorthand))
, sortUserCompany queryUser
, ("valid-until" , SortColumn $ queryQualUser >>> (E.?. QualificationUserValidUntil))
, ("last-refresh" , SortColumn $ queryQualUser >>> (E.?. QualificationUserLastRefresh))
, ("first-held" , SortColumn $ queryQualUser >>> (E.?. QualificationUserFirstHeld))
, ("blocked" , SortColumnNeverNull $ queryQualBlock >>> (E.?. QualificationUserBlockFrom))
, ("schedule-renew", SortColumnNullsInv $ queryQualUser >>> (E.?. QualificationUserScheduleRenewal))
-- , ("validity" , SortColumn $ queryQualUser >>> (E.?. QualificationUserValidUntil))
]
dbtFilter = mconcat
[ single $ fltrUserNameEmail queryUser
, single ("validity" , FilterColumn . E.mkExactFilterLast $ views (to queryQualUser) (validQualification' now))
, single ( "user-company", FilterColumn . E.mkExistsFilter $ \row criterion ->
dbtFilter = Map.fromList
[ fltrUserNameEmail queryUser
, ("validity" , FilterColumn . E.mkExactFilterLast $ views (to queryQualUser) (validQualification' now))
, ( "user-company", FilterColumn . E.mkExistsFilter $ \row criterion ->
E.from $ \(usrComp `E.InnerJoin` comp) -> do
let testname = (E.val criterion :: E.SqlExpr (E.Value (CI Text))) `E.isInfixOf`
(E.explicitUnsafeCoerceSqlExprValue "citext" (comp E.^. CompanyName) :: E.SqlExpr (E.Value (CI Text)))
@ -738,11 +734,9 @@ mkLicenceTable apidStatus rsChanged dbtIdent aLic apids = do
E.orderBy [E.desc countRows']
E.limit 7
pure (qblock E.^. QualificationUserBlockReason)
mkOption :: E.Value Text -> Option Text
mkOption (E.unValue -> t) = Option{ optionDisplay = t, optionInternalValue = t, optionExternalValue = toPathPiece t }
suggestionsBlock :: HandlerFor UniWorX (OptionList Text)
suggestionsBlock = mkOptionList . fmap mkOption <$> runDBRead (getBlockReasons E.not__)
suggestionsUnblock = mkOptionList . fmap mkOption <$> runDBRead (getBlockReasons id)
suggestionsBlock = mkOptionListText <$> runDBRead (getBlockReasons E.not__)
suggestionsUnblock = mkOptionListText <$> runDBRead (getBlockReasons id)
acts :: Map LicenceTableAction (AForm Handler LicenceTableActionData)
acts = mconcat
@ -811,7 +805,8 @@ postAdminAvsUserR uuid = do
mbContact <- try $ fmap fltrIdContact $ avsQuery $ AvsQueryContact $ Set.singleton $ AvsObjPersonId userAvsPersonId
-- mbStatus <- try $ fmap fltrIdStatus $ avsQuery $ AvsQueryStatus $ Set.singleton userAvsPersonId
mbStatus <- try $ queryAvsFullStatus userAvsPersonId -- TODO: delete Handler.Utils.Avs.lookupAvsUser if no longer needed -- NOTE: currently needed to provide card firms that are missing in AVS status query responses
let compsUsed :: [CompanyName] = stripCI <$> mbStatus ^.. _Right . _Wrapped . folded . _avsStatusPersonCardStatus . folded . _avsDataFirm . _Just
-- $logInfoS "AVS-1" [st|Status query for #{tshow userAvsPersonId} lieferte #{tshow mbStatus} |] -- DEBUG
let compsUsed :: [CompanyName] = mbStatus ^.. _Right . _Wrapped . folded . _avsStatusPersonCardStatus . folded . _avsDataFirm . _Just . to stripCI
compDict <- if 1 >= length compsUsed
then return mempty -- switch company only sensible if there is more than one company to choose
else do
@ -1004,15 +999,15 @@ getProblemAvsErrorR = do
E.on $ usravs E.^. UserAvsUser E.==. user E.^. UserId
E.where_ $ E.isJust $ usravs E.^. UserAvsLastSynchError
return (usravs, user) -- , E.substring (usravs E.^. UserAvsLastSynchError) (E.val ("'#\"%#\" %'") (E.val "#")) -- needs a different type on substring
qerryUsrAvs :: (E.SqlExpr (Entity UserAvs) `E.InnerJoin` E.SqlExpr (Entity User)) -> E.SqlExpr (Entity UserAvs)
qerryUsrAvs = $(E.sqlIJproj 2 1)
qerryUser :: (E.SqlExpr (Entity UserAvs) `E.InnerJoin` E.SqlExpr (Entity User)) -> E.SqlExpr (Entity User)
qerryUser = $(E.sqlIJproj 2 2)
querryUsrAvs :: (E.SqlExpr (Entity UserAvs) `E.InnerJoin` E.SqlExpr (Entity User)) -> E.SqlExpr (Entity UserAvs)
querryUsrAvs = $(E.sqlIJproj 2 1)
querryUser :: (E.SqlExpr (Entity UserAvs) `E.InnerJoin` E.SqlExpr (Entity User)) -> E.SqlExpr (Entity User)
querryUser = $(E.sqlIJproj 2 2)
reserrUsrAvs :: Lens' (DBRow (Entity UserAvs, Entity User)) (Entity UserAvs)
reserrUsrAvs = _dbrOutput . _1
-- reserrUser :: Lens' (DBRow (Entity UserAvs, Entity User)) (Entity User)
-- reserrUser = _dbrOutput . _2
dbtRowKey = qerryUsrAvs >>> (E.^. UserAvsId)
dbtRowKey = querryUsrAvs >>> (E.^. UserAvsId)
dbtProj = dbtProjId
dbtColonnade = dbColonnade $ mconcat
[ colUserNameModalHdrAdmin MsgLmsUser AdminUserR
@ -1025,15 +1020,15 @@ getProblemAvsErrorR = do
, sortable (Just "avs-last-error") (i18nCell MsgLastAvsSynchError)
$ cellMaybe textCell . view (reserrUsrAvs . _entityVal . _userAvsLastSynchError)
]
dbtSorting = mconcat
[ single (sortUserNameLink qerryUser)
, single ("avs-nr" , SortColumn $ qerryUsrAvs >>> (E.^. UserAvsNoPerson))
, single ("avs-last-synch", SortColumnNullsInv $ qerryUsrAvs >>> (E.^. UserAvsLastSynch))
, single ("avs-last-error", SortColumn $ qerryUsrAvs >>> (E.^. UserAvsLastSynchError))
dbtSorting = Map.fromList
[ sortUserNameLink querryUser
, ("avs-nr" , SortColumn $ querryUsrAvs >>> (E.^. UserAvsNoPerson))
, ("avs-last-synch", SortColumnNullsInv $ querryUsrAvs >>> (E.^. UserAvsLastSynch))
, ("avs-last-error", SortColumn $ querryUsrAvs >>> (E.^. UserAvsLastSynchError))
]
dbtFilter = mconcat
[ single $ fltrUserNameEmail qerryUser
, single ("avs-last-error", FilterColumn $ E.mkContainsFilterWithCommaPlus Just $ views (to qerryUsrAvs) (E.^. UserAvsLastSynchError))
dbtFilter = Map.fromList
[ fltrUserNameEmail querryUser
, ("avs-last-error", FilterColumn $ E.mkContainsFilterWithCommaPlus Just $ views (to querryUsrAvs) (E.^. UserAvsLastSynchError))
]
dbtFilterUI mPrev = mconcat
[ fltrUserNameEmailHdrUI MsgLmsUser mPrev

View File

@ -17,7 +17,7 @@ import Handler.Utils
-- import Data.Aeson (fromJSON)
-- import qualified Data.Aeson as Aeson
-- import qualified Data.Aeson.Types as Aeson
import qualified Data.Aeson.Encode.Pretty as Pretty
import qualified Data.Aeson.Encode.Pretty as Pretty
-- import qualified Data.CaseInsensitive as CI
import qualified Data.Text as Text
@ -37,13 +37,13 @@ import qualified Database.Esqueleto.Utils as E
-- Number of minutes a job must have been locked already to allow forced deletion
jobDeleteLockMinutes :: Int
jobDeleteLockMinutes = 3
jobDeleteLockMinutes = 3
deriveJSON defaultOptions
{ constructorTagModifier = camelToPathPiece' 1
} ''CronNextMatch
getAdminCrontabR :: Handler TypedContent
getAdminCrontabR = do
jState <- getsYesod appJobState
@ -64,7 +64,7 @@ getAdminCrontabR = do
encodeBearer =<< bearerToken (HashSet.singleton . Left $ toJSON UserGroupCrontab) Nothing (HashMap.singleton BearerTokenRouteEval $ HashSet.singleton AdminCrontabR) Nothing (Just Nothing) Nothing
siteLayoutMsg MsgHeadingAdminCrontab $ do
setTitleI MsgHeadingAdminCrontab
[whamlet|
@ -114,6 +114,7 @@ getAdminCrontabR = do
data JobTableAction = ActJobDelete
| ActJobSleep
deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic)
instance Universe JobTableAction
@ -121,9 +122,10 @@ instance Finite JobTableAction
nullaryPathPiece ''JobTableAction $ camelToPathPiece' 1
embedRenderMessage ''UniWorX ''JobTableAction id
newtype JobTableActionData = ActJobDeleteData
{ jobDeleteLocked :: Bool
}
data JobTableActionData
= ActJobDeleteData { jobDeleteLocked :: Bool }
| ActJobSleepData { jobSleepNr, jobSleepSecs :: Int
, jobSleepNow :: Bool }
deriving (Eq, Ord, Read, Show, Generic)
@ -151,7 +153,7 @@ postAdminJobsR = do
, 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
dbtSorting = Map.fromList
[ ("creation-time" , SortColumnNullsInv (E.^. QueuedJobCreationTime))
, ("job" , SortColumn (\v -> v E.^. QueuedJobContent E.->>. "job"))
, ("content" , SortColumn (E.^. QueuedJobContent))
@ -168,11 +170,20 @@ postAdminJobsR = do
prismAForm (singletonFilter "job" . maybePrism _PathPiece) mPrev $ aopt (hoistField lift textField) (fslI MsgTableJob)
]
dbtStyle = def { dbsFilterLayout = defaultDBSFilterLayout }
areq_posIntF msg = areq (posIntFieldI $ SomeMessages " " [SomeMessage msg, SomeMessage MsgMustBePositive]) (fslI msg)
acts :: Map JobTableAction (AForm Handler JobTableActionData)
acts = Map.singleton ActJobDelete $ ActJobDeleteData
<$> areq checkBoxField (fslI $ MsgActJobDeleteForce jobDeleteLockMinutes) Nothing
dbtParams = DBParamsForm
{ dbParamsFormAdditional =
acts = Map.fromList
[ (ActJobDelete, ActJobDeleteData
<$> areq checkBoxField (fslI $ MsgActJobDeleteForce jobDeleteLockMinutes) Nothing
),(ActJobSleep, ActJobSleepData
<$> areq_posIntF MsgJobSleepNr (Just 1)
<*> areq_posIntF MsgJobSleepSecs (Just 60)
<*> areq checkBoxField (fslI MsgJobSleepNow) (Just True)
)]
dbtParams = DBParamsForm
{ dbParamsFormAdditional =
renderAForm FormStandard
$ (, mempty) . First . Just
<$> multiActionA acts (fslI MsgTableAction) Nothing
@ -188,11 +199,11 @@ postAdminJobsR = do
dbtCsvDecode = Nothing
dbtExtraReps = []
-- jobsDBTableValidator :: PSValidator (MForm Handler) (FormResult (First JobTableAction, DBFormResult QueuedJobId Bool (DBRow (Entity QueuedJob))))
jobsDBTableValidator = def
jobsDBTableValidator = def
& defaultSorting [SortDescBy "creation-time"]
postprocess :: FormResult (First JobTableActionData, DBFormResult QueuedJobId Bool (DBRow (Entity QueuedJob)))
postprocess :: FormResult (First JobTableActionData, DBFormResult QueuedJobId Bool (DBRow (Entity QueuedJob)))
-> FormResult (JobTableActionData, Set QueuedJobId)
postprocess inp = do
postprocess inp = do
(First (Just act), jobMap) <- inp
let jobSet = Map.keysSet . Map.filter id $ getDBFormResult (const False) jobMap
return (act, jobSet)
@ -205,24 +216,40 @@ postAdminJobsR = do
cutoff = addUTCTime (nominalMinute * fromIntegral (negate jobDeleteLockMinutes)) now
jobReq = length jobIds
lockCriteria
| jobDeleteLocked =
| jobDeleteLocked =
[ QueuedJobLockTime ==. Nothing ] ||.
[ QueuedJobLockTime <=. Just cutoff ]
| otherwise =
| 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
(ActJobSleepData{..}, _) -> do
let jSleep = JobSleep jobSleepSecs
enqSleep = bool (void . queueJob) queueJob' jobSleepNow jSleep
replicateM_ jobSleepNr enqSleep
addMessageI Success (MsgTableJobActSleepFeedback jobSleepNr jobSleepSecs jobSleepNow)
reloadKeepGetParams AdminJobsR
-- gather some data on job worles
(nrWorkers, jobStateVar) <- getsYesod (view _appJobWorkers &&& appJobState)
jState <- atomically $ tryReadTMVar jobStateVar
let running = Map.size . jobWorkers <$> jState
siteLayoutMsg MsgMenuAdminJobs $ do
setTitleI MsgMenuAdminJobs
[whamlet|
^{jobsTable}
<section>
^{jobsTable}
<section>
<ul>
<li> #{running} job workers currently running
<li> #{nrWorkers} job workers configured to run
|]
where
doEnc :: ToJSON a => a -> _
@ -232,8 +259,8 @@ postAdminJobsR = do
, Text.splitOn "-" t
)
}
getJobName :: Value -> Maybe Text
getJobName (Object o)
getJobName (Object o)
| Just (String s) <- HashMap.lookup "job" o = Just s -- (kebabToCamel s)
getJobName _ = Nothing

View File

@ -1,4 +1,4 @@
-- SPDX-FileCopyrightText: 2022-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>, Gregor Kleen <gregor.kleen@ifi.lmu.de>, Steffen Jost <jost@tcs.ifi.lmu.de>
-- SPDX-FileCopyrightText: 2022-2025 Sarah Vaupel <sarah.vaupel@uniworx.de>, 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
@ -32,6 +32,8 @@ 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_)
{-# ANN module ("HLint: ignore Functor law" :: String) #-}
-- BEGIN - Buttons needed only here
data ButtonCreate = CreateMath | CreateInf | CrashApp -- Dummy for Example
deriving (Enum, Eq, Ord, Bounded, Read, Show, Generic)
@ -91,6 +93,7 @@ makeDemoForm n = identifyForm ("adminTestForm" :: Text) $ \html -> do
getAdminTestR, postAdminTestR :: Handler Html -- Demo Page. Referenzimplementierungen sollte hier gezeigt werden!
getAdminTestR = postAdminTestR
postAdminTestR = do
uid <- requireAuthId -- this is an admin-only route anyway
((btnResult, btnWdgt), btnEnctype) <- runFormPost $ identifyForm ("buttons" :: Text) (buttonForm :: Form ButtonCreate)
let btnForm = wrapForm btnWdgt def
{ formAction = Just $ SomeRoute AdminTestR
@ -99,7 +102,9 @@ postAdminTestR = do
}
case btnResult of
(FormSuccess CreateInf) -> addMessage Info "Informatik-Knopf gedrückt"
(FormSuccess CreateMath) -> addMessage Warning "Knopf Mathematik erkannt"
(FormSuccess CreateMath) -> do
void $ queueJob $ JobUserNotification { jRecipient = uid, jNotification = NotificationUserAuthModeUpdate uid }
addMessage Warning "Knopf Mathematik erkannt"
(FormSuccess CrashApp) -> addMessage Error "Crash Button Ratio 0 betätigt" >> error ("Crash Button" <> show (1 % 0))
FormMissing -> return ()
_other -> addMessage Warning "KEIN Knopf erkannt"

View File

@ -25,11 +25,6 @@ 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)
@ -119,17 +114,20 @@ mkCCTable = do
, 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
dbtFilter = Map.fromList
[ ("sentTo" , 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
, ("sentFrom" , FilterColumn . E.mkDayFilterFrom
$ \row -> E.coalesceDefault [queryPrint row E.?. PrintJobCreated, queryMail row E.?. SentMailSentAt] E.now_) -- either one is guaranteed to be non-null, default never used
, ("recipient" , FilterColumn . E.mkContainsFilterWithCommaPlus Just
$ \row -> E.coalesce [queryRecipientPrint row E.?. UserDisplayName, queryRecipientMail row E.?. UserDisplayName])
, ("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 "sentTo" . maybePrism _PathPiece) mPrev $ aopt (hoistField lift dayField) (fslI MsgTableFilterSentBefore)
, prismAForm (singletonFilter "sentFrom" . maybePrism _PathPiece) mPrev $ aopt (hoistField lift dayField) (fslI MsgTableFilterSentAfter)
, 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)
]

View File

@ -1,4 +1,4 @@
-- 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-FileCopyrightText: 2022-2024 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
@ -70,7 +70,7 @@ courseToForm (Entity cid Course{..}) lecs lecInvites qualis = CourseForm
, cfDeRegUntil = courseDeregisterUntil
, cfLecturers = [Right (lecturerUser, lecturerType) | Lecturer{..} <- lecs]
++ [Left (email, mType) | (email, InvDBDataLecturer mType) <- Map.toList lecInvites ]
-- TODO: Filterung nach aktueller Schule, da ansonsten ein Sicherheitleck droht! Siehe #150
-- TODO: Filterung nach aktueller Schule, da ansonsten ein Sicherheitleck droht! Siehe auch DevOps #1878
, cfQualis = [ (courseQualificationQualification, courseQualificationSortOrder)
| CourseQualification{..} <- qualis, courseQualificationCourse == cid ]
}
@ -452,6 +452,7 @@ courseEditHandler miButtonAction mbCourseForm = do
sinkInvitationsF lecturerInvitationConfig $ map (\(lEmail, mLty) -> (lEmail, cid, (InvDBDataLecturer mLty, InvTokenDataLecturer))) invites
void $ upsertCourseQualifications aid cid $ cfQualis res
insert_ $ CourseEdit aid now cid
memcachedInvalidateClass MemcachedKeyClassTutorialOccurrences
memcachedByInvalidate AuthCacheLecturerList $ Proxy @(Set UserId)
addMessageI Success $ MsgCourseEditOk tid ssh csh
return True
@ -470,7 +471,7 @@ 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
-- NOTE: CourseQualification allow the immediate assignment of these qualifications to any enrolled user. Hence SchoolAdmins must not be allowed to assign school-foreign qualifications here! Also see DevOps #1878
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:

View File

@ -31,10 +31,8 @@ postCEvDeleteR tid ssh csh cID = do
[whamlet|
$newline never
#{courseEventType}
$maybe room <- courseEventRoom
, #{roomReferenceText room}
:
^{occurrencesWidget courseEventTime}
^{occurrencesWidget False courseEventTime}
|]
drRecordConfirmString :: Entity CourseEvent -> DB Text

View File

@ -26,9 +26,8 @@ postCEvEditR tid ssh csh cID = do
replace eId CourseEvent
{ courseEventCourse
, courseEventType = cefType
, courseEventRoom = cefRoom
, courseEventRoomHidden = cefRoomHidden
, courseEventTime = cefTime
, courseEventTime = cefTime & JSONB
, courseEventNote = cefNote
, courseEventLastChanged = now
}

View File

@ -17,7 +17,6 @@ import qualified Database.Esqueleto.Legacy as E
data CourseEventForm = CourseEventForm
{ cefType :: CI Text
, cefRoom :: Maybe RoomReference
, cefRoomHidden :: Bool
, cefTime :: Occurrences
, cefNote :: Maybe StoredMarkup
@ -37,14 +36,12 @@ courseEventForm template = identifyForm FIDCourseEvent . renderWForm FormStandar
let courseEventTypes = optionsPairs [ (courseEventType, courseEventType) | Entity _ CourseEvent{..} <- existingEvents ]
cefType' <- wreq (textField & cfStrip & cfCI & addDatalist courseEventTypes) (fslI MsgCourseEventType & addPlaceholder (mr MsgCourseEventTypePlaceholder)) (cefType <$> template)
cefRoom' <- aFormToWForm $ roomReferenceFormOpt (fslI MsgCourseEventRoom) (cefRoom <$> template)
cefRoomHidden' <- wpopt checkBoxField (fslI MsgCourseEventRoomHidden & setTooltip MsgCourseEventRoomHiddenTip) (cefRoomHidden <$> template)
cefTime' <- aFormToWForm $ occurrencesAForm ("time" :: Text) (cefTime <$> template)
cefNote' <- wopt htmlField (fslI MsgCourseEventNote) (cefNote <$> template)
return $ CourseEventForm
<$> cefType'
<*> cefRoom'
<*> cefRoomHidden'
<*> cefTime'
<*> cefNote'
@ -52,8 +49,7 @@ courseEventForm template = identifyForm FIDCourseEvent . renderWForm FormStandar
courseEventToForm :: CourseEvent -> CourseEventForm
courseEventToForm CourseEvent{..} = CourseEventForm
{ cefType = courseEventType
, cefRoom = courseEventRoom
, cefRoomHidden = courseEventRoomHidden
, cefTime = courseEventTime
, cefTime = courseEventTime & unJSONB
, cefNote = courseEventNote
}

View File

@ -24,9 +24,8 @@ postCEventsNewR tid ssh csh = do
eId <- insert CourseEvent
{ courseEventCourse = cid
, courseEventType = cefType
, courseEventRoom = cefRoom
, courseEventRoomHidden = cefRoomHidden
, courseEventTime = cefTime
, courseEventTime = cefTime & JSONB
, courseEventNote = cefNote
, courseEventLastChanged = now
}

View File

@ -1,4 +1,4 @@
-- SPDX-FileCopyrightText: 2022-23 Sarah Vaupel <sarah.vaupel@ifi.lmu.de>, Steffen Jost <s.jost@fraport.de>
-- SPDX-FileCopyrightText: 2022-2024 Sarah Vaupel <sarah.vaupel@ifi.lmu.de>, Steffen Jost <s.jost@fraport.de>
--
-- SPDX-License-Identifier: AGPL-3.0-or-later
@ -13,6 +13,7 @@ import Import
import Handler.Utils
import Handler.Utils.Avs
import Handler.Utils.Company
import Jobs.Queue
@ -28,7 +29,7 @@ import Control.Monad.Except (MonadError(..))
import Generics.Deriving.Monoid (memptydefault, mappenddefault)
-- import Database.Esqueleto.Experimental ((:&)(..))
import Database.Esqueleto.Experimental ((:&)(..))
import qualified Database.Esqueleto.Experimental as E -- needs TypeApplications Lang-Pragma
import qualified Database.Esqueleto.Utils as E
@ -44,20 +45,22 @@ defaultTutorialType = "Schulung"
tutorialTypeSeparator :: TutorialType
tutorialTypeSeparator = "_"
tutorialTemplateNames :: Maybe TutorialType -> [TutorialType]
tutorialTemplateNames Nothing = ["Vorlage", "Template"]
tutorialTemplateNames (Just name) = [prefixes <> suffixes | prefixes <- tutorialTemplateNames Nothing, suffixes <- [mempty, tutorialTypeSeparator <> name]]
tutorialTypeSeparators :: [TutorialType]
tutorialTypeSeparators = tutorialTypeSeparator : ["-"]
tutorialTemplateNames :: [TutorialType]
tutorialTemplateNames = ["Vorlage", "Template"]
tutorialDefaultName :: Maybe TutorialType -> Day -> TutorialName
tutorialDefaultName Nothing = formatDayForTutName
tutorialDefaultName (Just ttyp) =
tutorialDefaultName Nothing = formatDayForTutName
tutorialDefaultName (Just ttyp) =
let prefix = CI.mk $ snd $ Text.breakOnEnd (CI.original tutorialTypeSeparator) $ CI.original ttyp
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
formatDayForTutName = CI.mk . Text.map d2u . Text.drop 2 . tshow
where
d2u '-' = '_'
d2u c = c
@ -151,7 +154,7 @@ instance Monoid AddParticipantsResult where
getCAddUserR, postCAddUserR :: TermId -> SchoolId -> CourseShorthand -> Handler Html
getCAddUserR = postCAddUserR
postCAddUserR tid ssh csh = do
postCAddUserR tid ssh csh = do
today <- localDay . TZ.utcToLocalTimeTZ appTZ <$> liftIO getCurrentTime
handleAddUserR tid ssh csh (Right today) Nothing
-- postTAddUserR tid ssh csh (CI.mk $ tshow today) -- Don't use user date display setting, so that tutorial default names conform to all users
@ -163,73 +166,71 @@ postTAddUserR tid ssh csh tutn = handleAddUserR tid ssh csh (Left tutn) Nothing
handleAddUserR :: TermId -> SchoolId -> CourseShorthand -> Either TutorialName Day -> Maybe TutorialType -> Handler Html
handleAddUserR tid ssh csh tdesc ttyp = do
(cid, tutTypes, tutNameSuggestions) <- runDB $ do
let plainTemplates = tutorialTemplateNames Nothing
cid <- getKeyBy404 $ TermSchoolCourseShort tid ssh csh
handleAddUserR tid ssh csh tdesc ttyp = do
(cEnt@Entity{entityKey=cid}, tutTypes, tutNameSuggestions) <- runDB $ do
cEnt@Entity{entityKey=cid} <- getBy404 $ TermSchoolCourseShort tid ssh csh
tutTypes <- E.select $ E.distinct $ do
tutorial <- E.from $ E.table @Tutorial
let tuTyp = tutorial E.^. TutorialType
E.where_ $ tutorial E.^. TutorialCourse E.==. E.val cid
E.orderBy [E.asc tuTyp]
return tuTyp
let typeSet = Set.fromList [ maybe t CI.mk $ Text.stripPrefix temp_sep $ CI.original t
| temp <- plainTemplates
, let temp_sep = CI.original (temp <> tutorialTypeSeparator)
, E.Value t <- tutTypes
let typeSet = Set.fromList [ maybe t CI.mk $ firstJust (`Text.stripPrefix` s) [CI.original $ tpl <> sep | sep <- tutorialTypeSeparators, tpl <- tutorialTemplateNames]
| E.Value t <- tutTypes, let s = CI.original t, t `notElem` tutorialTemplateNames
]
tutNames <- E.select $ do
tutNames <- E.select $ do
tutorial <- E.from $ E.table @Tutorial
let tuName = tutorial E.^. TutorialName
E.where_ $ tutorial E.^. TutorialCourse E.==. E.val cid
E.&&. E.isJust (tutorial E.^. TutorialFirstDay)
E.&&. E.not_ (E.any (E.hasPrefix_ (tutorial E.^. TutorialType) . E.val) plainTemplates)
E.orderBy [E.desc $ tutorial E.^. TutorialFirstDay, E.asc tuName]
E.&&. E.not_ (E.any (E.hasPrefix_ (tutorial E.^. TutorialType) . E.val) tutorialTemplateNames)
E.orderBy $ [E.asc $ tutorial E.^. TutorialName `E.hasInfix` E.val tn | tn <- tutorialTemplateNames] -- avoid template names, if possible
++ [E.desc $ tutorial E.^. TutorialFirstDay, E.asc tuName]
E.limit 7
return tuName
let tutNameSuggestions = return $ mkOptionList [Option tno tn tno | etn <- tutNames, let tn = E.unValue etn, let tno = CI.original tn]
return (cid, Set.toAscList typeSet, tutNameSuggestions) -- Set in order to remove duplicates and sort ascending at once
return (cEnt, Set.toAscList typeSet, tutNameSuggestions) -- Set in order to remove duplicates and sort ascending at once
currentRoute <- fromMaybe (error "postCAddUserR called from 404-handler") <$> getCurrentRoute
(_ , registerConfirmResult) <- runButtonForm FIDCourseRegisterConfirm
(_ , registerConfirmResult) <- runButtonForm FIDCourseRegisterConfirm
-- $logDebugS "***AbortProblem***" $ tshow registerConfirmResult
prefillUsers <- case registerConfirmResult of
prefillUsers <- case registerConfirmResult of
Nothing -> return mempty
(Just BtnCourseRegisterAbort) -> do
(Just BtnCourseRegisterAbort) -> do
addMessageI Warning MsgAborted
-- prefill confirmed users for convenience. Note that Browser-Back may also return to the filled form, but history.back() does not in Chrome
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
return $ Just $ Set.fromList $ fmap crActIdent confirmedActs
(Just BtnCourseRegisterConfirm) -> do
confirmedActs :: Set CourseRegisterActionData <- fmap Set.fromList . throwExceptT . mapMM encodedSecretBoxOpen . lookupPostParams $ toPathPiece PostCourseUserAddConfirmAction
confirmedActs :: Set CourseRegisterActionData <- fmap Set.fromList . throwExceptT . mapMM encodedSecretBoxOpen . lookupPostParams $ toPathPiece PostCourseUserAddConfirmAction
-- $logDebugS "CAddUserR confirmedActs" . tshow $ Set.map Aeson.encode confirmedActs
unless (Set.null confirmedActs) $ do -- TODO: check that all acts are member of availableActs
let
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!
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
tutId <- upsertNewTutorial cEnt 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 . identifyForm FIDCourseRegister . renderWForm FormStandard $ do
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) prefillUsers
auReqTutorial <- optionalActionW
( (,,)
( (,,)
<$> aopt (textField & cfStrip & cfCI & addDatalist tutNameSuggestions)
(fslI MsgCourseParticipantsRegisterTutorialField & setTooltip MsgCourseParticipantsRegisterTutorialFieldTip)
(Just $ maybeLeft tdesc)
<*> aopt (selectFieldList tutTypesMsg)
(fslI MsgTableTutorialType)
(fslI MsgCourseParticipantsTutorialType & setTooltip MsgCourseParticipantsTutorialTypeTooltip)
(Just tutDefType)
<*> aopt dayField
(fslI MsgTableTutorialFirstDay & setTooltip MsgCourseParticipantsRegisterTutorialFirstDayTip)
@ -343,22 +344,41 @@ registerUser cid (_avsIdent, Just uid) = exceptT return return $ do
return $ mempty { aurRegisterSuccess = Set.singleton uid }
upsertNewTutorial :: CourseId -> TutorialName -> Maybe TutorialType -> Maybe Day -> Handler TutorialId
upsertNewTutorial cid newTutorialName newTutorialType newFirstDay = runDB $ do
upsertNewTutorial :: Entity Course -> TutorialName -> Maybe TutorialType -> Maybe Day -> Handler TutorialId
upsertNewTutorial Entity{entityKey=cid, entityVal=crse} newTutorialName newTutorialType newFirstDay = runDB $ do
now <- liftIO getCurrentTime
existingTut <- getBy $ UniqueTutorial cid newTutorialName
templateEnt <- selectFirst [TutorialType <-. tutorialTemplateNames newTutorialType] [Desc TutorialType, Asc TutorialName]
-- templateEnt <- selectFirst [TutorialType <-. tutorialTemplateNames newTutorialType] [Desc TutorialType, Asc TutorialName] -- current prod as of 02/2025
templateEnt <- E.selectOne $ do
(tut :& crs :& trm) <- E.from $ E.table @Tutorial
`E.innerJoin` E.table @Course
`E.on` (\(tut :& crs) -> tut E.^. TutorialCourse E.==. crs E.^. CourseId)
`E.innerJoin` E.table @Term
`E.on` (\(_ :& crs :& trm) -> trm E.^. TermId E.==. crs E.^. CourseTerm)
E.where_ $ crs E.^. CourseSchool E.==. E.val (crse & courseSchool) -- filter by School
-- E.&&. tut E.^. TutorialName `E.in_` E.vals (tutorialTemplateNames newTutorialType) -- filter TutorialName being a template
E.orderBy $ -- NOTE: E.desc to have true before false, only works for non-nullable columns!
[ E.desc $ tut E.^. TutorialName `E.hasInfix` E.val pfx | pfx <- tutorialTemplateNames] -- prefer template names above all else
++ mcons ((\ttyp -> E.desc $ tut E.^. TutorialName `E.hasInfix` E.val ttyp) <$> newTutorialType) -- prefer ttype, if given.
[ E.desc $ tut E.^. TutorialCourse E.==. E.val cid -- prefer current course
, E.desc $ crs E.^. CourseName E.==. E.val (crse & courseName) -- prefer courses with identical name
, E.desc $ crs E.^. CourseShorthand E.==. E.val (crse & courseShorthand) -- prefer courses with identical shortcut
, E.desc $ crs E.^. CourseTerm E.==. E.val (crse & courseTerm) -- prefer courses from current term
, E.desc $ trm E.^. TermStart -- prefer most recently started term
-- , E.desc $ tut E.^. tutorialRegisterFrom
, E.asc $ tut E.^. TutorialName -- prefer tutorial name in alpahbetical order
]
return tut
case (existingTut, newFirstDay, templateEnt) of
(Just Entity{entityKey=tid},_,_) -> return tid -- no need to update, we ignore the anchor day
(Just Entity{entityKey=tid},_,_) -> return tid -- no need to update, we ignore the anchor day
(Nothing, Just moveDay, Just Entity{entityVal=Tutorial{..}}) -> do
Course{..} <- get404 cid
term <- get404 courseTerm
let oldFirstDay = fromMaybe moveDay $ tutorialFirstDay <|> fst (occurrencesBounds term tutorialTime)
newTime = normalizeOccurrences $ occurrencesAddBusinessDays term (oldFirstDay, moveDay) tutorialTime
term <- get404 $ courseTerm crse
let oldFirstDay = fromMaybe moveDay $ tutorialFirstDay <|> fst (occurrencesBounds term $ unJSONB tutorialTime)
newTime = normalizeOccurrences $ occurrencesAddBusinessDays term (oldFirstDay, moveDay) $ unJSONB tutorialTime
dayDiff = maybe 0 (diffDays moveDay) tutorialFirstDay
mvTime = fmap $ addLocalDays dayDiff
newType0 = CI.map (snd . Text.breakOnEnd (CI.original tutorialTypeSeparator)) tutorialType
newType = if newType0 `elem` tutorialTemplateNames Nothing
newType = if newType0 `elem` tutorialTemplateNames
then fromMaybe defaultTutorialType newTutorialType
else newType0
Entity tutId _ <- upsert
@ -367,13 +387,13 @@ upsertNewTutorial cid newTutorialName newTutorialType newFirstDay = runDB $ do
, tutorialCourse = cid
, tutorialType = newType
, tutorialFirstDay = newFirstDay
, tutorialTime = newTime
, tutorialTime = newTime & JSONB
, tutorialRegisterFrom = mvTime tutorialRegisterFrom
, tutorialRegisterTo = mvTime tutorialRegisterTo
, tutorialDeregisterUntil = mvTime tutorialDeregisterUntil
, tutorialLastChanged = now
, ..
} [] -- update cannot happen due to previous case
} [] -- update cannot happen due to previous case
audit $ TransactionTutorialEdit tutId
return tutId
_ -> do
@ -383,9 +403,8 @@ upsertNewTutorial cid newTutorialName newTutorialType newFirstDay = runDB $ do
, tutorialCourse = cid
, tutorialType = fromMaybe defaultTutorialType newTutorialType
, tutorialCapacity = Nothing
, tutorialRoom = Nothing
, tutorialRoomHidden = False
, tutorialTime = Occurrences mempty mempty
, tutorialTime = mempty
, tutorialRegGroup = Nothing
, tutorialRegisterFrom = Nothing
, tutorialRegisterTo = Nothing
@ -393,7 +412,7 @@ upsertNewTutorial cid newTutorialName newTutorialType newFirstDay = runDB $ do
, tutorialLastChanged = now
, tutorialTutorControlled = False
, tutorialFirstDay = Nothing
} [] -- update cannot happen due to previous cases
} [] -- update cannot happen due to previous cases
audit $ TransactionTutorialEdit tutId
return tutId
@ -401,6 +420,10 @@ registerTutorialMembers :: TutorialId -> Set UserId -> Handler ()
registerTutorialMembers tutId (Set.toList -> users) = runDB $ do
prevParticipants <- Set.fromList . fmap entityKey <$> selectList [TutorialParticipantUser <-. users, TutorialParticipantTutorial ==. tutId] []
participants <- fmap Set.fromList . for users $ \tutorialParticipantUser -> do
tutorialParticipantCompany <- selectCompanyUserPrime' tutorialParticipantUser
let tutorialParticipantDrivingPermit = Nothing
tutorialParticipantEyeExam = Nothing
tutorialParticipantNote = Nothing
Entity tutPartId _ <- upsert TutorialParticipant { tutorialParticipantTutorial = tutId, .. } []
audit $ TransactionTutorialParticipantEdit tutId tutPartId tutorialParticipantUser
return tutPartId

View File

@ -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>,Winnie Ros <winnie.ros@campus.lmu.de>
-- SPDX-FileCopyrightText: 2022-2024 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>,Sarah Vaupel <vaupel.sarah@campus.lmu.de>,Winnie Ros <winnie.ros@campus.lmu.de>,Steffen Jost <s.jost@fraport.de>
--
-- SPDX-License-Identifier: AGPL-3.0-or-later
@ -29,7 +29,7 @@ import Handler.Exam.List (mkExamTable)
getCShowR :: TermId -> SchoolId -> CourseShorthand -> Handler Html
getCShowR tid ssh csh = do
mbAid <- maybeAuthId
mbAid <- maybeAuthId
now <- liftIO getCurrentTime
(cid,course,courseVisible,schoolName,participants,registration,lecturers,assistants,administrators,correctors,tutors,news,events,submissionGroup,_mayReRegister,(mayViewSheets, mayViewAnySheet), (mayViewMaterials, mayViewAnyMaterial),courseQualifications) <- runDB . maybeT notFound $ do
[(E.Entity cid course, E.Value courseVisible, E.Value schoolName, E.Value participants, fmap entityVal -> registration)]
@ -146,7 +146,7 @@ getCShowR tid ssh csh = do
| otherwise
-> return . modal $(widgetFile "course/login-to-register") . Left . SomeRoute $ AuthR LoginR
registrationOpen <- hasWriteAccessTo $ CourseR tid ssh csh CRegisterR
mayMassRegister <- hasWriteAccessTo $ CourseR tid ssh csh CAddUserR
mayMassRegister <- hasWriteAccessTo $ CourseR tid ssh csh CAddUserR
MsgRenderer mr <- getMsgRenderer
@ -154,14 +154,14 @@ getCShowR tid ssh csh = do
tutorialDBTable = DBTable{..}
where
resultTutorial :: Lens' (DBRow (Entity Tutorial, Bool)) (Entity Tutorial)
resultTutorial = _dbrOutput . _1
resultShowRoom = _dbrOutput . _2
resultTutorial = _dbrOutput . _1
resultHideRoom = _dbrOutput . _2
dbtSQLQuery tutorial = do
E.where_ $ tutorial E.^. TutorialCourse E.==. E.val cid
let showRoom = maybe E.false (flip showTutorialRoom tutorial . E.val) mbAid
E.||. E.not_ (tutorial E.^. TutorialRoomHidden)
return (tutorial, showRoom)
let hideRoom = maybe E.true (E.not__ . flip showTutorialRoom tutorial . E.val) mbAid
E.&&. (tutorial E.^. TutorialRoomHidden)
return (tutorial, hideRoom)
dbtRowKey = (E.^. TutorialId)
dbtProj = over (_dbrOutput . _2) E.unValue <$> dbtProjId
dbtColonnade = dbColonnade $ mconcat
@ -180,10 +180,10 @@ getCShowR tid ssh csh = do
<li>
^{nameEmailWidget' tutor}
|]
, sortable (Just "room") (i18nCell MsgTableTutorialRoom) $ \res -> if
| res ^. resultShowRoom -> maybe (i18nCell MsgTableTutorialRoomIsUnset) roomReferenceCell $ views (resultTutorial . _entityVal) tutorialRoom res
| otherwise -> i18nCell MsgTableTutorialRoomIsHidden & addCellClass ("explanation" :: Text)
, sortable Nothing (i18nCell MsgTableTutorialTime) $ \(view $ resultTutorial . _entityVal -> Tutorial{..}) -> occurrencesCell tutorialTime
, sortable Nothing (i18nCell MsgTableTutorialOccurrence) $ \res ->
let roomHidden = res ^. resultHideRoom
ttime = res ^. resultTutorial . _entityVal . _tutorialTime
in occurrencesCell roomHidden ttime
, sortable (Just "register-from") (i18nCell MsgTutorialRegisterFrom) $ \(view $ resultTutorial . _entityVal -> Tutorial{..}) -> maybeDateTimeCell tutorialRegisterFrom
, sortable (Just "register-to") (i18nCell MsgTutorialRegisterTo) $ \(view $ resultTutorial . _entityVal -> Tutorial{..}) -> maybeDateTimeCell tutorialRegisterTo
, sortable (Just "deregister-until") (i18nCell MsgTableTutorialDeregisterUntil) $ \(view $ resultTutorial . _entityVal -> Tutorial{..}) -> maybeDateTimeCell tutorialDeregisterUntil
@ -220,7 +220,6 @@ getCShowR tid ssh csh = do
[ ("type", SortColumn $ \tutorial -> tutorial E.^. TutorialType )
, ("name", SortColumn $ \tutorial -> tutorial E.^. TutorialName )
, ("first-day", SortColumnNullsInv $ \tutorial -> tutorial E.^. TutorialFirstDay )
, ("room", SortColumn $ \tutorial -> tutorial E.^. TutorialRoom )
, ("register-from", SortColumn $ \tutorial -> tutorial E.^. TutorialRegisterFrom )
, ("register-to", SortColumn $ \tutorial -> tutorial E.^. TutorialRegisterTo )
, ("deregister-until", SortColumn $ \tutorial -> tutorial E.^. TutorialDeregisterUntil )

View File

@ -307,7 +307,7 @@ courseUserExamsSection (Entity cid Course{..}) (Entity uid _) = do
[ dbSelect (_2 . applying _2) _1 $ return . view (_dbrOutput . _1 . _entityKey)
, sortable (Just "name") (i18nCell MsgTableExamName) $ tellCell (Any True, mempty) . anchorCell' (\(view $ _dbrOutput . _1 . _entityVal -> Exam{..}) -> CExamR courseTerm courseSchool courseShorthand examName EShowR) (view $ _dbrOutput . _1 . _entityVal . _examName)
, sortable (Just "occurrence") (i18nCell MsgTableExamOccurrence) $ maybe mempty (cell . toWidget) . preview (_dbrOutput . _2 . _Just . _entityVal . _examOccurrenceName)
, sortable (Just "registration-time") (i18nCell MsgCourseExamRegistrationTime) $ maybe mempty (cell . formatTimeW SelFormatDateTime) . preview (_dbrOutput . _5 . _Just . _entityVal . _examRegistrationTime)
, sortable (Just "registration-time") (i18nCell MsgCourseExamRegistrationTime) $ foldMap dateTimeCell . preview (_dbrOutput . _5 . _Just . _entityVal . _examRegistrationTime)
, sortable (Just "bonus") (i18nCell MsgExamBonusAchieved) $ maybe mempty i18nCell . preview (_dbrOutput . _3 . _Just . _entityVal . _examBonusBonus)
, sortable (Just "result") (i18nCell MsgTableExamResult) $ maybe mempty i18nCell . preview (_dbrOutput . _4 . _Just . _entityVal . _examResultResult)
]
@ -444,13 +444,11 @@ courseUserTutorialsSection (Entity cid Course{..}) (Entity uid _) = do
<li>
^{userEmailWidget usr}
|]
, sortable (Just "room") (i18nCell MsgTableTutorialRoom) $ maybe (i18nCell MsgTableTutorialRoomIsUnset) roomReferenceCell . view (_dbrOutput . _1 . _entityVal . _tutorialRoom)
, sortable Nothing (i18nCell MsgTableTutorialTime) $ occurrencesCell . view (_dbrOutput . _1 . _entityVal . _tutorialTime)
, sortable Nothing (i18nCell MsgTableTutorialOccurrence) $ occurrencesCell False . view (_dbrOutput . _1 . _entityVal . _tutorialTime)
]
dbtSorting = mconcat
[ singletonMap "type" . SortColumn $ \(tutorial `E.InnerJoin` _) -> tutorial E.^. TutorialType
, singletonMap "name" . SortColumn $ \(tutorial `E.InnerJoin` _) -> tutorial E.^. TutorialName
, singletonMap "room" . SortColumn $ \(tutorial `E.InnerJoin` _) -> tutorial E.^. TutorialRoom
, singletonMap "tutors" . SortColumn $ \(tutorial `E.InnerJoin` _) -> E.subSelectMaybe . E.from $ \(tutor `E.InnerJoin` user) -> do
E.on $ tutor E.^. TutorUser E.==. user E.^. UserId
E.where_ $ tutorial E.^. TutorialId E.==. tutor E.^. TutorTutorial

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