diff --git a/messages/uniworx/categories/qualification/de-de-formal.msg b/messages/uniworx/categories/qualification/de-de-formal.msg index b5d3a36bc..f72efb50b 100644 --- a/messages/uniworx/categories/qualification/de-de-formal.msg +++ b/messages/uniworx/categories/qualification/de-de-formal.msg @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 Steffen Jost ,Steffen Jost +# SPDX-FileCopyrightText: 2022-25 Steffen Jost ,Steffen Jost # # SPDX-License-Identifier: AGPL-3.0-or-later @@ -7,20 +7,24 @@ QualificationName: Qualifikation QualificationDescription: Beschreibung QualificationValidIndicator: Gültigkeit QualificationValidDuration: Gültigkeitsdauer -QualificationAuditDuration: Aufbewahrung Audit Log +QualificationAuditDuration: Aufbewahrungszeitraum E‑Learning Log QualificationAuditDurationTooltip n@Int: Optionaler Zeitraum zur Löschung von E‑Learning Daten. Hinweis: Der E‑Learning Server kann seine anonymisierten Daten schon früher löschen, aber spätestens #{n} Tage nach Abschluss. -QualificationAuditDurationReuseError: Diese Qualifikation nutzt das E‑Learning einer anderen Qualifikation, für die derzeit keinen Löschzeitraum konfiguriert wurde. +QualificationAuditDurationReuseNoTime: Diese Qualifikation nutzt das E‑Learning einer anderen Qualifikation, für die derzeit keinen Löschzeitraum konfiguriert wurde. +QualificationAuditDurationReuseError: Fehler: Aufbewahrungszeitraum E‑Learning Log kann nicht individuell konfiguriert werden, wenn das E‑Learning einer anderen Qualifikation mitbenutzt wird. QualificationRefreshWithin: Erneurerungszeitraum QualificationRefreshWithinTooltip: Optionaler Zeitraum vor Ablauf für eine Benachrichtigung per Email. Bei aktiviertem automatischem E‑Learning wird dieses gestartet und die Benachrichtigung erfolgt per Brief oder Email. QualificationRefreshReminder: Zweite Erinnerung QualificationRefreshReminderTooltip: Optionaler Zeitraum vor Ablauf zur Versendung einer zweiten Erinnerung per Brief oder Email mit identischen E‑Learning Zugangsdaten, sofern die Qualifikation noch gültig und das E‑Learning noch offen ist. QualificationElearningStart: Wird das E‑Learning automatisch gestartet? QualificationElearningRenew: Verlängert ein erfolgreiches E‑Learning die Qualifikation automatisch um die reguläre Gültigkeitsdauer? -QualificationElearningLimit: Ist die Anzahl der E‑Learning Versuche limitiert? +QualificationElearningLimit: Limit Anzahl E‑Learning Versuche +QualificationElearningLimitExplain: Ist die Anzahl der E‑Learning 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 @@ -98,8 +102,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: E‑Learning wird nicht automatisch gestartet, da die Zeitspanne für den Erneurerungszeitraum nicht festgelegt wurde! -LmsErrorNoRenewElearning: Fehler: Erfoglreiches E‑Learning verlängert die Qualifikation nicht automatisch, da die Gültigkeitsdauer nicht festgelegt wurde! +LmsErrorNoRefreshElearning: Fehler: E‑Learning wird nicht automatisch gestartet, da die Zeitspanne für den Erneuerungszeitraum nicht festgelegt wurde! +LmsErrorNoRenewElearning: Fehler: Erfolgreiches E‑Learning verlängert die Qualifikation nicht automatisch, da die Gültigkeitsdauer nicht festgelegt wurde! MailSubjectQualificationRenewal qname@Text: Qualifikation #{qname} muss demnächst erneuert werden MailSubjectQualificationExpiry qname@Text: Qualifikation #{qname} läuft demnächst ab MailSubjectQualificationExpired qname@Text: Qualifikation #{qname} ist ab sofort ungültig @@ -147,3 +151,10 @@ LmsActionFailed n@Int: Aktion nicht durchgeführt für #{n} #{pluralDE n "Person LmsStarted: E‑Learning eröffnet BtnLmsEnqueue: Nutzer mit ablaufenden Qualifikationen zum E‑Learning anmelden und benachrichtigen BtnLmsDequeue: Nutzer mit beendetem E‑Learning 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 Institutsangabe. Bitte versuchen Sie erneut, nachdem Sie Seite neu geladen haben. \ No newline at end of file diff --git a/messages/uniworx/categories/qualification/en-eu.msg b/messages/uniworx/categories/qualification/en-eu.msg index b8fb5c38e..d5121b735 100644 --- a/messages/uniworx/categories/qualification/en-eu.msg +++ b/messages/uniworx/categories/qualification/en-eu.msg @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2022 Steffen Jost ,Steffen Jost +# SPDX-FileCopyrightText: 2022-25 Steffen Jost ,Steffen Jost # # SPDX-License-Identifier: AGPL-3.0-or-later @@ -9,18 +9,22 @@ QualificationValidIndicator: Validity QualificationValidDuration: Validity period QualificationAuditDuration: Audit log retention period QualificationAuditDurationTooltip n@Int: Optional period for deletion of e‑learning data. Note that the e‑learning server may delete its anonymised data earlier, at most #{n} days after closing. -QualificationAuditDurationReuseError: This qualification reuses the e‑learning from another qualification, which has no audit duration configured. +QualificationAuditDurationReuseNoTime: This qualification reuses the e‑learning from another qualification, which has no audit duration configured. +QualificationAuditDurationReuseError: Error: Audit log retention period may not be configure when reusing the e‑learning from another qualification. QualificationRefreshWithin: Refresh within QualificationRefreshWithinTooltip: Optional period before expiry to send a notification by email. If e‑learning is set to start automatically, it will be started and e‑learning credentials are send with this notification by post or email. QualificationRefreshReminder: 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 e‑learning is still undecided and the qualification has not yet expired. QualificationElearningStart: Is e‑learning automatically started? QualificationElearningRenew: Does successful e‑learning automatically extend a qualification by the default validity period? -QualificationElearningLimit: Is the number of e‑learning attempts limited? +QualificationElearningLimit: Limit of e‑learning attempts +QualificationElearningLimitExplain: Is the number of e‑learning 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 @@ -147,3 +151,10 @@ LmsActionFailed n: No action for #{n} #{pluralENs n "person"}, since there was n LmsStarted: E‑learning open since BtnLmsEnqueue: Enqueue users with expiring qualifications for e‑learning and notify them BtnLmsDequeue: Dequeue users with finished e‑learning 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 data mismatch on institute detected. Please try again after reloading the page. \ No newline at end of file diff --git a/messages/uniworx/utils/utils/de-de-formal.msg b/messages/uniworx/utils/utils/de-de-formal.msg index b706ef0eb..2a20302a3 100644 --- a/messages/uniworx/utils/utils/de-de-formal.msg +++ b/messages/uniworx/utils/utils/de-de-formal.msg @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2023 Steffen Jost ,Gregor Kleen ,Sarah Vaupel ,Winnie Ros +# SPDX-FileCopyrightText: 2023-25 Steffen Jost ,Gregor Kleen ,Sarah Vaupel ,Winnie Ros ,Steffen Jost # # SPDX-License-Identifier: AGPL-3.0-or-later @@ -104,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 diff --git a/messages/uniworx/utils/utils/en-eu.msg b/messages/uniworx/utils/utils/en-eu.msg index 028c09adb..54c41bd5b 100644 --- a/messages/uniworx/utils/utils/en-eu.msg +++ b/messages/uniworx/utils/utils/en-eu.msg @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2023 Sarah Vaupel ,Winnie Ros ,Steffen Jost +# SPDX-FileCopyrightText: 2023-25 Sarah Vaupel ,Winnie Ros ,Steffen Jost ,Steffen Jost # # SPDX-License-Identifier: AGPL-3.0-or-later @@ -104,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 diff --git a/models/lms.model b/models/lms.model index 50b686fcf..d47a7f0f2 100644 --- a/models/lms.model +++ b/models/lms.model @@ -4,21 +4,21 @@ 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 Maybe -- 6 if > 0, number of months to keep audit log and LmsUserIdents; or indefinitely (dangerous, since LmsIdents may run out) + 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 diff --git a/src/Handler/LMS.hs b/src/Handler/LMS.hs index 5b00547d8..923a41623 100644 --- a/src/Handler/LMS.hs +++ b/src/Handler/LMS.hs @@ -1,4 +1,4 @@ --- SPDX-FileCopyrightText: 2022-24 Sarah Vaupel ,Steffen Jost ,Steffen Jost +-- SPDX-FileCopyrightText: 2022-25 Sarah Vaupel ,Steffen Jost ,Steffen Jost -- -- SPDX-License-Identifier: AGPL-3.0-or-later @@ -156,7 +156,7 @@ mkLmsAllTable isAdmin lmsDeletionDays = do foldMap (textCell . formatCalendarDiffDays . fromMonths) . view (resultAllQualification . _qualificationAuditDuration) , sortable (Just "qel-renew") (i18nCell MsgTableLmsElearningRenews & cellTooltip MsgQualificationElearningRenew) $ tickmarkCell . view (resultAllQualification . _qualificationElearningRenews) - , sortable (Just "qel-limit") (i18nCell MsgTableLmsElearningLimit & cellTooltip MsgQualificationElearningLimit) + , sortable (Just "qel-limit") (i18nCell MsgTableLmsElearningLimit & cellTooltip MsgQualificationElearningLimitExplain) $ cellMaybe numCell . view (resultAllQualification . _qualificationElearningLimit) , sortable (Just "qel-reuse") (i18nCell MsgTableQualificationLmsReuses & cellTooltip MsgTableQualificationLmsReusesTooltip) $ \(view (resultAllQualification . _qualificationLmsReuses) -> reuseQid) -> maybeCell reuseQid qualificationIdShortCell diff --git a/src/Handler/PrintCenter.hs b/src/Handler/PrintCenter.hs index b9cd28d6a..c50dc7dd5 100644 --- a/src/Handler/PrintCenter.hs +++ b/src/Handler/PrintCenter.hs @@ -1,4 +1,4 @@ --- SPDX-FileCopyrightText: 2022-24 Sarah Vaupel ,Steffen Jost ,Steffen Jost +-- SPDX-FileCopyrightText: 2022-25 Sarah Vaupel ,Steffen Jost ,Steffen Jost -- -- SPDX-License-Identifier: AGPL-3.0-or-later @@ -37,7 +37,7 @@ import Handler.Utils import qualified Data.CaseInsensitive as CI import Jobs.Queue -import qualified Control.Monad.State.Class as State +-- import qualified Control.Monad.State.Class as State -- for debug only data LRQF = LRQF { lrqfLetter :: Text @@ -68,8 +68,8 @@ makeRenewalForm tmpl = identifyForm FIDLmsLetter . validateForm validateLetterRe validateLetterRenewQualification :: FormValidator LRQF Handler () validateLetterRenewQualification = do - LRQF{..} <- State.get - liftHandler $ addMessage Warning $ text2Html $ "Validate called:" <> tshow lrqfUser -- DEBUG + -- LRQF{..} <- State.get + -- liftHandler $ addMessage Warning $ text2Html $ "Validate called:" <> tshow lrqfUser -- DEBUG return () lrqf2letter :: LRQF -> DB (Entity User, SomeLetter) diff --git a/src/Handler/Qualification.hs b/src/Handler/Qualification.hs index 14a0fdf75..007ef6812 100644 --- a/src/Handler/Qualification.hs +++ b/src/Handler/Qualification.hs @@ -146,11 +146,6 @@ mkQualificationAllTable isAdmin = do dbTable resultDBTableValidator resultDBTable - --- getQualificationEditR, postQualificationEditR :: SchoolId -> QualificationShorthand -> Handler Html --- getQualificationEditR = postQualificationEditR --- postQualificationEditR = error "TODO" - data QualificationTableCsv = QualificationTableCsv -- Q..T..C.. -> qtc.. { qtcDisplayName :: UserDisplayName , qtcEmail :: UserEmail diff --git a/src/Handler/Qualification/Edit.hs b/src/Handler/Qualification/Edit.hs index 1f0e6d87e..32dc35dfa 100644 --- a/src/Handler/Qualification/Edit.hs +++ b/src/Handler/Qualification/Edit.hs @@ -13,6 +13,9 @@ module Handler.Qualification.Edit import Import +import qualified Data.Text as Text +import qualified Control.Monad.State.Class as State + import Handler.Utils -- import Database.Esqueleto.Experimental ((:&)(..)) -- import qualified Database.Esqueleto.Experimental as E -- needs TypeApplications Lang-Pragma @@ -29,24 +32,82 @@ postQualificationEditR ssh qsh = do handleQualificationEdit ssh $ Just qent -mkQualificationForm :: Maybe Qualification -> SchoolId -> Qualification -mkQualificationForm templ ssh = renderAForm FormStandard $ Qualification -- to reorder form fiels, use permuteFun on Qualification - <$> areq hiddenField "" (Just ssh) - <*> areq ciField (fslI MsgQualificationShort) (qualificationShorthand <$> templ) - <*> areq ciField (fslI MsgQualificationName) (qualificationName <$> templ) - <*> aopt htmlField (fslI MsgQualificationDescription) (qualificationDescription <$> templ) - <*> aopt (posIntFieldI MsgQualificationValidDuration) (fslI MsgQualificationValidDuration) (qualificationValidDuration <$> templ) - <*> aopt (posIntFieldI MsgQualificationAuditDuration) (fslI MsgQualificationAuditDuration) (qualificationAuditDuration <$> templ) - <*> aopt calendarDiffDaysField (fslI MsgQualificationRefreshWithin) (qualificationRefreshWithin <$> templ) - <*> aopt calendarDiffDaysField (fslI MsgQualificationRefreshReminder) (qualificationRefreshReminder <$> templ) - <*> areq checkBoxField (fslI MsgQualificationElearningStart) (qualificationElearningStart <$> templ) - <*> areq checkBoxField (fslI MsgTableQualificationElearningRenews) (qualificationElearningRenews <$> templ) - <*> aopt (posIntFieldI MsgQualificationElearningLimit)(fslI MsgQualificationElearningLimit) (qualificationElearningLimit <$> templ) - <*> aopt (error "TODO") (fslI MsgTableQualificationLmsReuses) (qualificationLmsReuses <$> templ) - <*> areq checkBoxField (fslI MsgQualificationExpiryNotification) (qualificationExpiryNotification <$> templ) - <*> aopt (error "TODO") (fslI MsgQualificationAvsLicence) (qualificationAvsLicence <$> templ) - <*> aopt textField (fslI MsgQualficiationSapId) (qualificationSapId <$> templ) - -- TODO: add tooltips +mkQualificationForm :: SchoolId -> Maybe Qualification -> Form Qualification +mkQualificationForm ssh templ = identifyForm FIDQualificationEdit . validateForm (validateQualificationEdit ssh) $ \html -> + flip (renderAForm FormStandard) html $ reorderedQualification + <$> areq hiddenField "" (Just ssh) -- 1 -> 1 + <*> areq ciField (fslI MsgQualificationShort) (qualificationShorthand <$> templ) -- 2 -> 2 + <*> areq ciField (fslI MsgQualificationName) (qualificationName <$> templ) -- 3 -> 3 + <*> aopt htmlField (fslI MsgQualificationDescription) (qualificationDescription <$> templ) -- 4 -> 4 + <*> aopt_natFieldI MsgQualificationValidDuration (qualificationValidDuration <$> templ) -- 5 -> 5 + <*> aopt calendarDiffDaysField (fslI MsgQualificationRefreshWithin & + setTooltip MsgQualificationRefreshWithinTooltip) (qualificationRefreshWithin <$> templ) -- 6 -> 7 + + <*> areq checkBoxField (fslI MsgQualificationElearningStart) (qualificationElearningStart <$> templ) -- 7 -> 9 + <*> aopt calendarDiffDaysField (fslI MsgQualificationRefreshReminder & + setTooltip MsgQualificationRefreshReminderTooltip) (qualificationRefreshReminder <$> templ) -- 8 -> 8 + <*> areq checkBoxField (fslI MsgQualificationExpiryNotification) (qualificationExpiryNotification <$> templ) -- 9 -> 13 + <*> aopt_natFieldI MsgQualificationAuditDuration (qualificationAuditDuration <$> templ) -- 10 -> 6 + <*> areq checkBoxField (fslI MsgQualificationElearningRenew) (qualificationElearningRenews <$> templ) -- 11 -> 10 + <*> aopt_natFieldI MsgQualificationElearningLimit (qualificationElearningLimit <$> templ) -- 12 -> 11 + <*> aopt qualificationField (fslI MsgTableQualificationLmsReuses & + setTooltip MsgTableQualificationLmsReusesTooltip) (qualificationLmsReuses <$> templ) -- 13 -> 12 + <*> aopt avsLicenceField (fslI MsgQualificationAvsLicence & + setTooltip MsgTableQualificationIsAvsLicenceTooltip) (qualificationAvsLicence <$> templ) -- 14 -> 14 + <*> aopt textField (fslI MsgQualificationSapId & + setTooltip MsgTableQualificationSapExportTooltip) (qualificationSapId <$> templ) -- 15 -> 15 + where + avsLicenceField :: Field Handler AvsLicence + avsLicenceField = selectFieldList [ (Text.singleton $ licence2char lic, lic) | lic <- universeF, lic /= AvsNoLicence ] + + aopt_natFieldI msg = aopt (natFieldI $ UniWorXMessages [SomeMessage msg, text2message " ", SomeMessage MsgMustBePositive]) (fslI msg) + -- [ 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15] + reorderedQualification = $(permuteFun [ 1, 2, 3, 4, 5,10, 6, 8, 7,11,12,13, 9,14,15]) Qualification -- read reverse: at the 6th position, the 10th element should be placed +validateQualificationEdit :: SchoolId -> FormValidator Qualification Handler () +validateQualificationEdit ssh = do + canonise + Qualification{..} <- State.get + guardValidation MsgQualFormErrorSshMismatch $ qualificationSchool == ssh + guardValidation MsgLmsErrorNoRefreshElearning $ not qualificationElearningStart || isJust qualificationRefreshWithin + guardValidation MsgLmsErrorNoRenewElearning $ not qualificationElearningStart || isJust qualificationValidDuration + guardValidation MsgQualificationAuditDurationReuseError $ isNothing qualificationAuditDuration || isNothing qualificationLmsReuses + where + canonise = do -- i.e. map Just 0 to Nothing + Qualification{..} <- State.get + -- canonisation, i.e. map Just 0 to Nothing + when (qualificationRefreshWithin == Just mempty) $ State.modify $ set _qualificationRefreshWithin Nothing + when (qualificationRefreshReminder == Just mempty) $ State.modify $ set _qualificationRefreshReminder Nothing + when (qualificationValidDuration == Just 0) $ State.modify $ set _qualificationValidDuration Nothing + when (qualificationAuditDuration == Just 0) $ State.modify $ set _qualificationAuditDuration Nothing + when (qualificationElearningLimit == Just 0) $ State.modify $ set _qualificationElearningLimit Nothing + + handleQualificationEdit :: SchoolId -> Maybe (Entity Qualification) -> Handler Html -handleQualificationEdit _ _ = error "todo" \ No newline at end of file +handleQualificationEdit ssh templ = do + ((qRes, qWgt), qEnc) <- runFormPost $ mkQualificationForm ssh $ entityVal <$> templ + let qForm = wrapForm qWgt def + { formEncoding = qEnc + } + formResult qRes $ \resQuali -> do + uniqViolation <- runDB $ case templ of + Just Entity{entityKey=qid} -> replaceUnique qid resQuali -- edit old qualification + _ -> maybeM (checkUnique resQuali) (const $ return Nothing) (insertUnique resQuali) -- insert new qualification + case uniqViolation of + Just (SchoolQualificationShort _ nconflict) -> addMessageI Error $ MsgQualFormErrorDuplShort $ ciOriginal nconflict + Just (SchoolQualificationName _ nconflict) -> addMessageI Error $ MsgQualFormErrorDuplName $ ciOriginal nconflict + Nothing -> do + let qshort = qualificationShorthand resQuali + qmsg = if isNothing templ then MsgQualificationCreated else MsgQualificationEdit + addMessageI Success $ qmsg $ ciOriginal qshort + redirect $ QualificationR ssh qshort + let heading = bool MsgMenuQualificationNew MsgMenuQualificationEdit $ isJust templ + siteLayoutMsg heading $ do + setTitleI heading + [whamlet| +

+ ^{qForm} + $maybe _ <- templ +

+ _{MsgQualificationEditNote} + |] \ No newline at end of file diff --git a/src/Handler/Utils/Form.hs b/src/Handler/Utils/Form.hs index ca770354b..a21eb208b 100644 --- a/src/Handler/Utils/Form.hs +++ b/src/Handler/Utils/Form.hs @@ -1,4 +1,4 @@ --- SPDX-FileCopyrightText: 2022-24 Felix Hamann ,Gregor Kleen ,Sarah Vaupel ,Sarah Vaupel ,Steffen Jost ,Winnie Ros ,Steffen Jost +-- SPDX-FileCopyrightText: 2022-25 Felix Hamann ,Gregor Kleen ,Sarah Vaupel ,Sarah Vaupel ,Steffen Jost ,Winnie Ros ,Steffen Jost -- -- SPDX-License-Identifier: AGPL-3.0-or-later @@ -1425,6 +1425,7 @@ utcTimeField = checkMMap (return . localTimeToUTC') utcToLocalTime localTimeFiel LTUNone{} -> Left MsgIllDefinedUTCTime LTUAmbiguous{} -> Left MsgAmbiguousUTCTime +-- | Field for entering calendarDiffDays, result (0, 0) is accepted here calendarDiffDaysField :: (MonadHandler m, HandlerSite m ~ UniWorX) => Field m CalendarDiffDays calendarDiffDaysField = Field { fieldParse = parseDD @@ -1434,7 +1435,7 @@ calendarDiffDaysField = Field [whamlet| $newline never - _{MsgSomeMonths} + _{MsgSomeMonths} # _{MsgSomeDays} |] @@ -1447,12 +1448,11 @@ calendarDiffDaysField = Field parseDD [tmon, tday] _ | Just nmon <- readMay tmon , Just nday <- readMay tday - -- , 0 =< nmon + nday - = return $ Right $ if 0 == nmon + nday -- TODO: this should not be distinguished here - then Nothing - else Just $ CalendarDiffDays { cdMonths=nmon, cdDays=nday} - parseDD [] _ = return $ Right Nothing - parseDD _ _ = return $ Left "Parsing calendarDiffDaysField failed" -- TODO: better error messages + = return $ Right $ Just $ CalendarDiffDays { cdMonths=nmon, cdDays=nday} + parseDD ["",""] _ = return $ Right Nothing + parseDD [""] _ = return $ Right Nothing + parseDD [] _ = return $ Right Nothing + parseDD pl _ = return $ Left $ SomeMessage $ "Parsing CalendarDiffDays failed: " <> tshow pl -- TODO: better error message langField :: Bool -- ^ Only allow values from `appLanguages` diff --git a/src/Utils/Form.hs b/src/Utils/Form.hs index f49f3a883..c2ddc1bb4 100644 --- a/src/Utils/Form.hs +++ b/src/Utils/Form.hs @@ -1,4 +1,4 @@ --- SPDX-FileCopyrightText: 2023 Felix Hamann ,Gregor Kleen ,Sarah Vaupel ,Sarah Vaupel ,Steffen Jost ,Steffen Jost ,Wolfgang Witt +-- SPDX-FileCopyrightText: 2023-25 Felix Hamann ,Gregor Kleen ,Sarah Vaupel ,Sarah Vaupel ,Steffen Jost ,Steffen Jost ,Wolfgang Witt ,Steffen Jost -- -- SPDX-License-Identifier: AGPL-3.0-or-later @@ -330,6 +330,7 @@ data FormIdentifier | FIDFirmAction | FIDTutorialExamOccurrences | FIDUnreachableUsersAction + | FIDQualificationEdit deriving (Eq, Ord, Read, Show) instance PathPiece FormIdentifier where diff --git a/src/Utils/TH.hs b/src/Utils/TH.hs index 83e560af5..46629ad78 100644 --- a/src/Utils/TH.hs +++ b/src/Utils/TH.hs @@ -149,11 +149,10 @@ with -- Functions -- --------------- -permuteFun :: [Int] -> ExpQ -- generic permutation of function arguments, i.e. $(permuteFun [2,1]) == flip +permuteFun :: [Int] -> ExpQ -- generic permutation of function arguments, i.e. $(permuteFun [2,1]) == flip -- list determins position where the argument comes from permuteFun perm = lamE pat rhs where pat = map varP $ fn:xs rhs = foldl appE (varE fn) $ map varE ps --- rhs = appE (varE fn) (varE $ xs!!1) ln = length perm xs = [ mkName $ "x" ++ show j | j <- [1..ln] ] ps = [ xs !! (j-1) | j <- perm ] @@ -163,7 +162,6 @@ altFun :: [Int] -> ExpQ -- generic permutation/repetition of function arguments, altFun perm = lamE pat rhs where pat = map varP $ fn:xs rhs = foldl appE (varE fn) $ map varE ps --- rhs = appE (varE fn) (varE $ xs!!1) mx = maximum $ impureNonNull perm xs = [ mkName $ "x" ++ show j | j <- [1..mx] ] ps = [ xs !! (j-1) | j <- perm ] diff --git a/templates/lms.hamlet b/templates/lms.hamlet index a6db19331..877ed34c2 100644 --- a/templates/lms.hamlet +++ b/templates/lms.hamlet @@ -1,6 +1,6 @@ $newline never -$# SPDX-FileCopyrightText: 2022-23 Sarah Vaupel ,Steffen Jost +$# SPDX-FileCopyrightText: 2022-25 Sarah Vaupel ,Steffen Jost ,Steffen Jost $# $# SPDX-License-Identifier: AGPL-3.0-or-later @@ -63,7 +63,7 @@ $# SPDX-License-Identifier: AGPL-3.0-or-later $if (qualificationElearningRenews quali) && isNothing (qualificationValidDuration quali)

#{icon IconNotificationError} - _{MsgLmsErrorNoRefreshElearning} + _{MsgLmsErrorNoRenewElearning}

_{MsgQualificationElearningLimit}
diff --git a/templates/qualification.hamlet b/templates/qualification.hamlet index 23a89530f..36a01e246 100644 --- a/templates/qualification.hamlet +++ b/templates/qualification.hamlet @@ -1,6 +1,6 @@ $newline never -$# SPDX-FileCopyrightText: 2022 Sarah Vaupel ,Steffen Jost +$# SPDX-FileCopyrightText: 2022-25 Sarah Vaupel ,Steffen Jost ,Steffen Jost $# $# SPDX-License-Identifier: AGPL-3.0-or-later @@ -22,7 +22,7 @@ $# SPDX-License-Identifier: AGPL-3.0-or-later $maybe daudit <- qualificationAuditDuration lqre _{MsgMonths (fromIntegral daudit)} $nothing - _{MsgQualificationAuditDurationReuseError} + _{MsgQualificationAuditDurationReuseNoTime} $nothing _{MsgMonths (fromIntegral daudit)} $nothing @@ -67,7 +67,7 @@ $# SPDX-License-Identifier: AGPL-3.0-or-later $if (qualificationElearningRenews quali) && isNothing (qualificationValidDuration quali)

#{icon IconNotificationError} - _{MsgLmsErrorNoRefreshElearning} + _{MsgLmsErrorNoRenewElearning}

_{MsgQualificationElearningLimit}