diff --git a/messages/uniworx/categories/courses/courses/de-de-formal.msg b/messages/uniworx/categories/courses/courses/de-de-formal.msg index e1e39aa94..53cc9d2d9 100644 --- a/messages/uniworx/categories/courses/courses/de-de-formal.msg +++ b/messages/uniworx/categories/courses/courses/de-de-formal.msg @@ -72,6 +72,7 @@ CourseEditOk tid@TermId ssh@SchoolId csh@CourseShorthand: Kursart #{tid}-# 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 CourseLecturer: Kursverwalter:in MailSubjectParticipantInvitation tid@TermId ssh@SchoolId csh@CourseShorthand: [#{tid}-#{ssh}-#{csh}] Einladung zur Kursartteilnahme CourseParticipantInviteHeading courseName@Text: Einladung zum Kursartteilnahmer für #{courseName} diff --git a/messages/uniworx/categories/courses/courses/en-eu.msg b/messages/uniworx/categories/courses/courses/en-eu.msg index 43ea7c45e..ed44433f7 100644 --- a/messages/uniworx/categories/courses/courses/en-eu.msg +++ b/messages/uniworx/categories/courses/courses/en-eu.msg @@ -72,6 +72,7 @@ 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. 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 CourseLecturer: Course administrator MailSubjectParticipantInvitation tid ssh csh: [#{tid}-#{ssh}-#{csh}] Invitaion to join the course CourseParticipantInviteHeading courseName: Invitation to enrol for #{courseName} diff --git a/messages/uniworx/misc/de-de-formal.msg b/messages/uniworx/misc/de-de-formal.msg index 7fa240fe6..07758a5d6 100644 --- a/messages/uniworx/misc/de-de-formal.msg +++ b/messages/uniworx/misc/de-de-formal.msg @@ -29,4 +29,5 @@ PaginationSize: Einträge pro Seite PaginationPage: Angzeigte Seite PaginationError: Paginierung Parameter dürfen nicht negativ sein -NullDeletes: Zum Löschen NULL eingeben. \ No newline at end of file +NullDeletes: Zum Löschen NULL eingeben. +SortPriority: Sortierungspriorität \ No newline at end of file diff --git a/messages/uniworx/misc/en-eu.msg b/messages/uniworx/misc/en-eu.msg index e7142f4bc..54d519125 100644 --- a/messages/uniworx/misc/en-eu.msg +++ b/messages/uniworx/misc/en-eu.msg @@ -29,4 +29,5 @@ PaginationSize: Rows per Page PaginationPage: Page to show PaginationError: Pagination parameter must not be negative -NullDeletes: Enter NULL to delete. \ No newline at end of file +NullDeletes: Enter NULL to delete. +SortPriority: Sort order priority \ No newline at end of file diff --git a/src/Handler/Course/Edit.hs b/src/Handler/Course/Edit.hs index bfec8a864..d8519968b 100644 --- a/src/Handler/Course/Edit.hs +++ b/src/Handler/Course/Edit.hs @@ -85,13 +85,23 @@ makeCourseForm miButtonAction template = identifyForm FIDcourse . validateFormDB MsgRenderer mr <- getMsgRenderer uid <- liftHandler requireAuthId - (lecturerSchools, adminSchools, oldSchool) <- liftHandler . runDB $ do - lecturerSchools <- map (userFunctionSchool . entityVal) <$> selectList [UserFunctionUser ==. uid, UserFunctionFunction <-. [SchoolLecturer]] [] - protoAdminSchools <- map (userFunctionSchool . entityVal) <$> selectList [UserFunctionUser ==. uid, UserFunctionFunction <-. [SchoolAdmin]] [] + (userSchools, elegibleQualifications) :: ([SchoolId], OptionList QualificationId) <- liftHandler . runDB $ do + lecturerSchools <- map (userFunctionSchool . entityVal) <$> selectList [UserFunctionUser ==. uid, UserFunctionFunction <-. [SchoolLecturer]] [] + protoAdminSchools <- map (userFunctionSchool . entityVal) <$> selectList [UserFunctionUser ==. uid, UserFunctionFunction <-. [SchoolAdmin]] [] adminSchools <- filterM (hasWriteAccessTo . flip SchoolR SchoolEditR) protoAdminSchools oldSchool <- forM (cfCourseId =<< template) $ fmap courseSchool . getJust - return (lecturerSchools, adminSchools, oldSchool) - let userSchools = nubOrd . maybe id (:) oldSchool $ lecturerSchools ++ adminSchools + let elegibleSchools = Set.fromList $ lecturerSchools ++ adminSchools + userSchools = Set.toList $ maybe id Set.insert oldSchool elegibleSchools + q2opt :: Entity Qualification -> Option QualificationId + q2opt (Entity qid Qualification{..}) = + let qsh = CI.original $ unSchoolKey qualificationSchool + in Option{ optionDisplay = CI.original qualificationName <> " (" <> qsh <> ")" + , optionExternalValue = "(" <> CI.original qualificationShorthand <> "___" <> qsh <> ")" + , optionInternalValue = qid + } + elegibleQualifications <- selectList [QualificationSchool <-. Set.toList elegibleSchools] [Asc QualificationName, Asc QualificationSchool] + return (userSchools, mkOptionList (map q2opt elegibleQualifications)) + (termsField, userTerms) <- liftHandler $ case template of -- Change of term is only allowed if user may delete the course (i.e. no participants) or admin @@ -166,6 +176,31 @@ makeCourseForm miButtonAction template = identifyForm FIDcourse . validateFormDB miIdent :: Text miIdent = "lecturers" + qualificationsForm :: Maybe [(QualificationId, Int)] -> AForm Handler [(QualificationId, Int)] -- filter by admin school done later through upsertCourseQualifications + qualificationsForm = massInputAccumEditA miAdd miEdit miButtonAction miLayout miIdent (fslI $ MsgCourseQualifications 9) False + where + miIdent :: Text + miIdent = "qualifications" + + miAdd :: (Text -> Text) -> FieldView UniWorX -> Form ([(QualificationId,Int)] -> FormResult [(QualificationId,Int)]) + miAdd nudge submitView csrf = do + (formRes, formView) <- aCourseQualiForm nudge Nothing csrf + let addRes = formRes <&> \newDat (Set.fromList -> oldDat) -> if + | newDat `Set.member` oldDat -> FormFailure [mr MsgCourseEditQualificationFailExists] + | otherwise -> FormSuccess $ pure newDat + return (addRes, $(widgetFile "widgets/massinput/courseQualifications/add")) + + miEdit :: (Text -> Text) -> (QualificationId, Int) -> Form (QualificationId,Int) + miEdit nudge = aCourseQualiForm nudge . Just + + miLayout :: MassInputLayout ListLength (QualificationId,Int) (QualificationId, Int) + miLayout lLength _ cellWdgts delButtons addWdgts = $(widgetFile "widgets/massinput/courseQualifications/layout") + + aCourseQualiForm :: (Text -> Text) -> Maybe (QualificationId, Int) -> Form (QualificationId, Int) + aCourseQualiForm nudge mTemplate csrf = do + (cquRes, cquView) <- mpreq (selectField $ pure elegibleQualifications) ("" & addName (nudge "cquali")) (view _1 <$> mTemplate) + (ordRes, ordView) <- mpreq intField ("" & addName (nudge "cqordr")) (view _2 <$> mTemplate) + return ((,) <$> cquRes <*> ordRes, $(widgetFile "widgets/massinput/courseQualifications/form")) (newVisFrom,newRegFrom,newRegTo,newDeRegUntil) <- case template of (Just cform) | (Just _cid) <- cfCourseId cform -> return (Nothing,Nothing,Nothing,Nothing) @@ -212,7 +247,7 @@ makeCourseForm miButtonAction template = identifyForm FIDcourse . validateFormDB & setTooltip MsgCourseDeregisterUntilTip) (deepAlt (cfDeRegUntil <$> template) newDeRegUntil) <* aformSection MsgCourseFormSectionAdministration <*> lecturerForm - <*> pure mempty -- TODO: continue here !!! -- TODO: Filterung nach aktueller Schule, da ansonsten ein Sicherheitleck droht! Siehe #150 + <*> qualificationsForm (cfQualis <$> template) return (result, widget) diff --git a/templates/widgets/massinput/courseQualifications/add.hamlet b/templates/widgets/massinput/courseQualifications/add.hamlet new file mode 100644 index 000000000..6903dab9f --- /dev/null +++ b/templates/widgets/massinput/courseQualifications/add.hamlet @@ -0,0 +1,9 @@ +$newline never + +$# SPDX-FileCopyrightText: 2024 Sarah Vaupel , Steffen Jost +$# +$# SPDX-License-Identifier: AGPL-3.0-or-later + +^{formView} + + ^{fvWidget submitView} diff --git a/templates/widgets/massinput/courseQualifications/form.hamlet b/templates/widgets/massinput/courseQualifications/form.hamlet new file mode 100644 index 000000000..8e286195c --- /dev/null +++ b/templates/widgets/massinput/courseQualifications/form.hamlet @@ -0,0 +1,11 @@ +$newline never + +$# SPDX-FileCopyrightText: 2024 Sarah Vaupel , Steffen Jost +$# +$# SPDX-License-Identifier: AGPL-3.0-or-later + + + #{csrf} + ^{fvWidget cquView} + + ^{fvWidget ordView} diff --git a/templates/widgets/massinput/courseQualifications/layout.hamlet b/templates/widgets/massinput/courseQualifications/layout.hamlet new file mode 100644 index 000000000..c137daf67 --- /dev/null +++ b/templates/widgets/massinput/courseQualifications/layout.hamlet @@ -0,0 +1,21 @@ +$newline never + +$# SPDX-FileCopyrightText: 2024 Sarah Vaupel , Steffen Jost +$# +$# SPDX-License-Identifier: AGPL-3.0-or-later + + + + + + $forall coord <- review liveCoords lLength + + ^{cellWdgts ! coord} + + + ^{addWdgts ! (0, 0)}
_{MsgTableQualification} + _{MsgSortPriority} + +
+ ^{fvWidget (delButtons ! coord)} +