diff --git a/messages/uniworx/de-de-formal.msg b/messages/uniworx/de-de-formal.msg index 13d8acbbc..ea682ebe4 100644 --- a/messages/uniworx/de-de-formal.msg +++ b/messages/uniworx/de-de-formal.msg @@ -403,6 +403,7 @@ UnauthorizedSchoolAdmin: Sie sind nicht als Administrator für dieses Institut e UnauthorizedAdminEscalation: Sie sind nicht Administrator für alle Institute, für die dieser Nutzer Administrator oder Veranstalter ist. UnauthorizedExamOffice: Sie sind nicht Teil eines Prüfungsamts. UnauthorizedExamExamOffice: Es existieren keine Prüfungsergebnisse für Nutzer, für die Sie Teil eines assoziierten Prüfungsamts sind. +UnauthorizedExternalExamExamOffice: Es existieren keine Prüfungsergebnisse für Nutzer, für die Sie Teil eines assoziierten Prüfungsamts sind. UnauthorizedSchoolLecturer: Sie sind nicht als Veranstalter für dieses Institut eingetragen. UnauthorizedLecturer: Sie sind nicht als Veranstalter für diese Veranstaltung eingetragen. UnauthorizedAllocationLecturer: Sie sind nicht als Veranstalter für eine Veranstaltung dieser Zentralanmeldung eingetragen. @@ -449,6 +450,8 @@ UnauthorizedTutor: Sie sind nicht Tutor. UnauthorizedTutorialRegisterGroup: Sie sind bereits in einem Tutorium mit derselben Registrierungs-Gruppe. UnauthorizedLDAP: Angegebener Nutzer meldet sich nicht mit Campus-Kennung an. UnauthorizedPWHash: Angegebener Nutzer meldet sich nicht mit Uni2work-Kennung an. +UnauthorizedExternalExamListNotEmpty: Liste von externen Prüfungen ist nicht leer +UnauthorizedExternalExamLecturer: Sie sind nicht als Prüfer für diese externe Prüfung eingetragen UnauthorizedPasswordResetToken: Dieses Authorisierungs-Token kann nicht mehr zum Passwort ändern benutzt werden @@ -1190,6 +1193,11 @@ MenuCourseNewsNew: Neue Kursnachricht MenuCourseNewsEdit: Kursnachricht bearbeiten MenuCourseEventNew: Neuer Kurstermin MenuCourseEventEdit: Kurstermin bearbeiten +MenuExternalExamGrades: Prüfungsleistungen +MenuExternalExamUsers: Teilnehmer +MenuExternalExamEdit: Bearbeiten +MenuExternalExamNew: Neue externe Prüfung +MenuExternalExamList: Externe Prüfungen BreadcrumbSubmissionFile: Datei BreadcrumbSubmissionUserInvite: Einladung zur Abgabe @@ -1242,6 +1250,13 @@ BreadcrumbExamRegister: Anmelden BreadcrumbApplicationFiles: Bewerbungsdateien BreadcrumbCourseNewsArchive: Archiv BreadcrumbCourseNewsFile: Datei +BreadcrumbExternalExam: Externe Prüfung +BreadcrumbExternalExamList: Externe Prüfungen +BreadcrumbExternalExamNew: Neue externe Prüfung +BreadcrumbExternalExamShow coursen@CourseName examn@ExamName: #{coursen}, #{examn} +BreadcrumbExternalExamEdit: Editieren +BreadcrumbExternalExamUsers: Teilnehmer +BreadcrumbExternalExamGrades: Prüfungsleistungen TitleMetrics: Metriken diff --git a/messages/uniworx/en-eu.msg b/messages/uniworx/en-eu.msg index b49b918f8..be28d263d 100644 --- a/messages/uniworx/en-eu.msg +++ b/messages/uniworx/en-eu.msg @@ -447,6 +447,7 @@ UnauthorizedTutor: You are no tutor. UnauthorizedTutorialRegisterGroup: You are already registered for a tutorial with the same registration group. UnauthorizedLDAP: Specified user does not log in with their campus account. UnauthorizedPWHash: Specified user does not log in with an Uni2work-account. +UnauthorizedExternalExamListNotEmpty: List of external exams is not empty UnauthorizedPasswordResetToken: This authorisation-token may no longer be used to change passwords @@ -1189,6 +1190,11 @@ MenuCourseNewsNew: Add course news MenuCourseNewsEdit: Edit course news MenuCourseEventNew: New course occurrence MenuCourseEventEdit: Edit course occurrence +MenuExternalExamGrades: Exam results +MenuExternalExamUsers: Participants +MenuExternalExamEdit: Edit +MenuExternalExamNew: New external exam +MenuExternalExamList: External exams BreadcrumbSubmissionFile: File BreadcrumbSubmissionUserInvite: Invitation to participate in a submission @@ -1241,6 +1247,13 @@ BreadcrumbExamRegister: Register BreadcrumbApplicationFiles: Application files BreadcrumbCourseNewsArchive: Archive BreadcrumbCourseNewsFile: File +BreadcrumbExternalExam: External exam +BreadcrumbExternalExamList: External exams +BreadcrumbExternalExamNew: New external exam +BreadcrumbExternalExamShow coursen@CourseName examn@ExamName: #{coursen}, #{examn} +BreadcrumbExternalExamEdit: Edit +BreadcrumbExternalExamUsers: Participants +BreadcrumbExternalExamGrades: Exam results TitleMetrics: Metrics diff --git a/models/exam-office.model b/models/exam-office.model index dc952c26f..ab45e3abd 100644 --- a/models/exam-office.model +++ b/models/exam-office.model @@ -11,4 +11,9 @@ ExamOfficeResultSynced school SchoolId Maybe office UserId result ExamResultId + time UTCTime +ExamOfficeExternalResultSynced + school SchoolId Maybe + office UserId + result ExternalExamResultId time UTCTime \ No newline at end of file diff --git a/models/external-exams.model b/models/external-exams.model new file mode 100644 index 000000000..bd203fa0d --- /dev/null +++ b/models/external-exams.model @@ -0,0 +1,23 @@ +ExternalExam + term TermId + school SchoolId + courseName (CI Text) + examName (CI Text) + defaultTime UTCTime Maybe + showGrades Bool + UniqueExternalExam term school courseName examName +ExternalExamResult + user UserId + exam ExternalExamId + result ExamResultGrade + time UTCTime + lastChanged UTCTime + UniqueExternalExamResult exam user +ExternalExamStaff + user UserId + exam ExternalExamId + UniqueExternalExamStaff exam user +ExternalExamOfficeSchool + school SchoolId + exam ExternalExamId + UniqueExternalExamOfficeSchool exam school \ No newline at end of file diff --git a/routes b/routes index 2304d6d34..c9e55436f 100644 --- a/routes +++ b/routes @@ -80,6 +80,15 @@ /users EOUsersR GET POST /users/invite EOUsersInviteR GET POST +/external-exam EExamListR GET !lecturer !¬empty +/external-exam/new EExamNewR GET POST !lecturer +/external-exam/#TermId/#SchoolId/#CourseName/#ExamName EExamR !lecturer: + / EEShowR GET !exam-office + /edit EEEditR GET POST + /users EEUsersR GET POST + /grades EEGradesR GET POST !exam-office + + /term TermShowR GET !free /term/current TermCurrentR GET !free /term/edit TermEditR GET POST diff --git a/src/Application.hs b/src/Application.hs index 3f4fa2e66..cc7d70d4d 100644 --- a/src/Application.hs +++ b/src/Application.hs @@ -122,6 +122,7 @@ import Handler.Exam import Handler.Allocation import Handler.ExamOffice import Handler.Metrics +import Handler.ExternalExam -- This line actually creates our YesodDispatch instance. It is the second half diff --git a/src/Foundation.hs b/src/Foundation.hs index 07cd31a94..0ac1d885a 100644 --- a/src/Foundation.hs +++ b/src/Foundation.hs @@ -69,6 +69,7 @@ import qualified Control.Monad.Catch as C import Handler.Utils.StudyFeatures import Handler.Utils.SchoolLdap import Handler.Utils.ExamOffice.Exam +import Handler.Utils.ExamOffice.ExternalExam import Handler.Utils.ExamOffice.Course import Handler.Utils.Profile import Handler.Utils.Routes @@ -371,6 +372,19 @@ tagAccessPredicate AuthExamOffice = APDB $ \mAuthId route _ -> case route of E.where_ $ examOfficeExamResultAuth (E.val authId) examResult guardMExceptT hasUsers (unauthorizedI MsgUnauthorizedExamExamOffice) return Authorized + EExamR tid ssh coursen examn _ -> $cachedHereBinary (mAuthId, tid, ssh, coursen, examn) . exceptT return return $ do + authId <- maybeExceptT AuthenticationRequired $ return mAuthId + hasUsers <- lift . E.selectExists . E.from $ \(eexam `E.InnerJoin` eexamResult) -> do + E.on $ eexam E.^. ExternalExamId E.==. eexamResult E.^. ExternalExamResultExam + + E.where_ $ eexam E.^. ExternalExamTerm E.==. E.val tid + E.&&. eexam E.^. ExternalExamSchool E.==. E.val ssh + E.&&. eexam E.^. ExternalExamCourseName E.==. E.val coursen + E.&&. eexam E.^. ExternalExamExamName E.==. E.val examn + + E.where_ $ examOfficeExternalExamResultAuth (E.val authId) eexamResult + guardMExceptT hasUsers $ unauthorizedI MsgUnauthorizedExternalExamExamOffice + return Authorized _other -> $cachedHereBinary mAuthId . exceptT return return $ do authId <- maybeExceptT AuthenticationRequired $ return mAuthId adrights <- lift $ selectFirst [UserFunctionUser ==. authId, UserFunctionFunction ==. SchoolExamOffice] [] @@ -420,7 +434,18 @@ tagAccessPredicate AuthLecturer = APDB $ \mAuthId route _ -> case route of E.&&. allocation E.^. AllocationTerm E.==. E.val tid E.&&. allocation E.^. AllocationSchool E.==. E.val ssh E.&&. allocation E.^. AllocationShorthand E.==. E.val ash - guardMExceptT isLecturer (unauthorizedI MsgUnauthorizedAllocationLecturer) + guardMExceptT isLecturer $ unauthorizedI MsgUnauthorizedAllocationLecturer + return Authorized + EExamR tid ssh coursen examn _ -> $cachedHereBinary (mAuthId, tid, ssh, coursen, examn) . exceptT return return $ do + authId <- maybeExceptT AuthenticationRequired $ return mAuthId + isLecturer <- lift . E.selectExists . E.from $ \(eexam `E.InnerJoin` staff) -> do + E.on $ eexam E.^. ExternalExamId E.==. staff E.^. ExternalExamStaffExam + E.where_ $ staff E.^. ExternalExamStaffUser E.==. E.val authId + E.&&. eexam E.^. ExternalExamTerm E.==. E.val tid + E.&&. eexam E.^. ExternalExamSchool E.==. E.val ssh + E.&&. eexam E.^. ExternalExamCourseName E.==. E.val coursen + E.&&. eexam E.^. ExternalExamExamName E.==. E.val examn + guardMExceptT isLecturer $ unauthorizedI MsgUnauthorizedExternalExamLecturer return Authorized -- lecturer for any school will do _ -> $cachedHereBinary mAuthId . exceptT return return $ do @@ -1033,7 +1058,14 @@ tagAccessPredicate AuthRegisterGroup = APDB $ \mAuthId route _ -> case route of guard $ not hasOther return Authorized r -> $unsupportedAuthPredicate AuthRegisterGroup r -tagAccessPredicate AuthEmpty = APDB $ \_ route _ -> case route of +tagAccessPredicate AuthEmpty = APDB $ \mAuthId route _ -> case route of + EExamListR -> exceptT return return $ do + authId <- maybeExceptT AuthenticationRequired $ return mAuthId + hasExternalExams <- $cachedHereBinary authId . lift . E.selectExists . E.from $ \(eexam `E.InnerJoin` eexamStaff) -> do + E.on $ eexam E.^. ExternalExamId E.==. eexamStaff E.^. ExternalExamStaffExam + E.where_ $ eexamStaff E.^. ExternalExamStaffUser E.==. E.val authId + guardMExceptT (not hasExternalExams) $ unauthorizedI MsgUnauthorizedExternalExamListNotEmpty + return Authorized CourseR tid ssh csh _ -> maybeT (unauthorizedI MsgCourseNotEmpty) $ do -- Entity cid Course{..} <- MaybeT . getBy $ TermSchoolCourseShort tid ssh csh cid <- $cachedHereBinary (tid, ssh, csh) . MaybeT . getKeyBy $ TermSchoolCourseShort tid ssh csh @@ -1500,8 +1532,9 @@ siteLayout' headingOverride widget = do where go crumbs Nothing = return crumbs go crumbs (Just cRoute) = do + hasAccess <- hasReadAccessTo cRoute (title, next) <- breadcrumb cRoute - go ((cRoute, title) : crumbs) next + go ((cRoute, title, hasAccess) : crumbs) next (title, parents) <- breadcrumbs' mcurrentRoute -- let isParent :: Route UniWorX -> Bool @@ -1588,7 +1621,7 @@ siteLayout' headingOverride widget = do } let highlight :: Route UniWorX -> Bool -- highlight last route in breadcrumbs, favorites taking priority - highlight = let crumbs = mcons mcurrentRoute $ fst <$> reverse parents + highlight = let crumbs = mcons mcurrentRoute $ view _1 <$> reverse parents navItems = map (view _2) favourites ++ map (urlRoute . menuItemRoute . view _1) menuTypes highR = find (`elem` navItems) . uncurry (++) $ partition (`elem` map (view _2) favourites) crumbs in \r -> Just r == highR @@ -1933,6 +1966,25 @@ instance YesodBreadcrumbs UniWorX where breadcrumb MessageListR = i18nCrumb MsgMenuMessageList $ Just AdminR breadcrumb GlossaryR = i18nCrumb MsgMenuGlossary $ Just InfoR + + breadcrumb EExamListR = i18nCrumb MsgMenuExternalExamList Nothing + breadcrumb EExamNewR = do + isEO <- hasReadAccessTo $ ExamOfficeR EOExamsR + i18nCrumb MsgBreadcrumbExternalExamNew . Just $ if + | isEO -> ExamOfficeR EOExamsR + | otherwise -> EExamListR + breadcrumb (EExamR tid ssh coursen examn sRoute) = case sRoute of + EEShowR -> do + isEO <- hasReadAccessTo $ ExamOfficeR EOExamsR + maybeT (i18nCrumb MsgBreadcrumbExternalExam . Just $ bool EExamListR (ExamOfficeR EOExamsR) isEO) $ do + guardM . hasReadAccessTo $ EExamR tid ssh coursen examn EEShowR + i18nCrumb (MsgBreadcrumbExternalExamShow coursen examn) . Just $ if + | isEO -> ExamOfficeR EOExamsR + | otherwise -> EExamListR + EEEditR -> i18nCrumb MsgBreadcrumbExternalExamEdit . Just $ EExamR tid ssh coursen examn EEShowR + EEUsersR -> i18nCrumb MsgBreadcrumbExternalExamUsers . Just $ EExamR tid ssh coursen examn EEShowR + EEGradesR -> i18nCrumb MsgBreadcrumbExternalExamGrades . Just $ EExamR tid ssh coursen examn EEShowR + -- breadcrumb _ = return ("Uni2work", Nothing) -- Default is no breadcrumb at all submissionList :: TermId -> CourseShorthand -> SheetName -> UserId -> DB [E.Value SubmissionId] @@ -2119,6 +2171,14 @@ pageActions (HomeR) = , menuItemModal = False , menuItemAccessCallback' = return True } + , MenuItem + { menuItemType = PageActionSecondary + , menuItemLabel = MsgMenuExternalExamList + , menuItemIcon = Nothing + , menuItemRoute = SomeRoute EExamListR + , menuItemModal = False + , menuItemAccessCallback' = return True + } , MenuItem { menuItemType = PageActionPrime , menuItemLabel = MsgMenuOpenCourses @@ -3055,6 +3115,62 @@ pageActions (CorrectionsGradeR) = return $ orOf (traverse . _Value . _submissionModeCorrector) sheets } ] +pageActions EExamListR = + [ MenuItem + { menuItemType = PageActionPrime + , menuItemLabel = MsgMenuExternalExamNew + , menuItemIcon = Nothing + , menuItemRoute = SomeRoute EExamNewR + , menuItemModal = False + , menuItemAccessCallback' = return True + } + ] +pageActions (EExamR tid ssh coursen examn EEShowR) = + [ MenuItem + { menuItemType = PageActionPrime + , menuItemLabel = MsgMenuExternalExamEdit + , menuItemIcon = Nothing + , menuItemRoute = SomeRoute $ EExamR tid ssh coursen examn EEEditR + , menuItemModal = False + , menuItemAccessCallback' = return True + } + , MenuItem + { menuItemType = PageActionPrime + , menuItemLabel = MsgMenuExternalExamUsers + , menuItemIcon = Nothing + , menuItemRoute = SomeRoute $ EExamR tid ssh coursen examn EEUsersR + , menuItemModal = False + , menuItemAccessCallback' = return True + } + , MenuItem + { menuItemType = PageActionPrime + , menuItemLabel = MsgMenuExternalExamGrades + , menuItemIcon = Nothing + , menuItemRoute = SomeRoute $ EExamR tid ssh coursen examn EEGradesR + , menuItemModal = False + , menuItemAccessCallback' = return True + } + ] +pageActions (EExamR tid ssh coursen examn EEGradesR) = + [ MenuItem + { menuItemType = PageActionPrime + , menuItemLabel = MsgMenuExternalExamUsers + , menuItemIcon = Nothing + , menuItemRoute = SomeRoute $ EExamR tid ssh coursen examn EEUsersR + , menuItemModal = False + , menuItemAccessCallback' = return True + } + ] +pageActions (EExamR tid ssh coursen examn EEUsersR) = + [ MenuItem + { menuItemType = PageActionPrime + , menuItemLabel = MsgMenuExternalExamGrades + , menuItemIcon = Nothing + , menuItemRoute = SomeRoute $ EExamR tid ssh coursen examn EEGradesR + , menuItemModal = False + , menuItemAccessCallback' = return True + } + ] pageActions _ = [] diff --git a/src/Foundation/Routes.hs b/src/Foundation/Routes.hs index 0e83a0734..afe77ba0e 100644 --- a/src/Foundation/Routes.hs +++ b/src/Foundation/Routes.hs @@ -31,6 +31,7 @@ deriving instance Generic SubmissionR deriving instance Generic MaterialR deriving instance Generic TutorialR deriving instance Generic ExamR +deriving instance Generic EExamR deriving instance Generic CourseApplicationR deriving instance Generic AllocationR deriving instance Generic SchoolR diff --git a/src/Handler/ExamOffice.hs b/src/Handler/ExamOffice.hs index 5ad3a8bda..de787dfd7 100644 --- a/src/Handler/ExamOffice.hs +++ b/src/Handler/ExamOffice.hs @@ -6,3 +6,4 @@ import Handler.ExamOffice.Exams as Handler.ExamOffice import Handler.ExamOffice.Fields as Handler.ExamOffice import Handler.ExamOffice.Users as Handler.ExamOffice import Handler.ExamOffice.Exam as Handler.ExamOffice +import Handler.ExamOffice.ExternalExam as Handler.ExamOffice diff --git a/src/Handler/ExamOffice/ExternalExam.hs b/src/Handler/ExamOffice/ExternalExam.hs new file mode 100644 index 000000000..2b114c2cf --- /dev/null +++ b/src/Handler/ExamOffice/ExternalExam.hs @@ -0,0 +1,10 @@ +module Handler.ExamOffice.ExternalExam + ( getEEGradesR, postEEGradesR + ) where + +import Import + + +getEEGradesR, postEEGradesR :: TermId -> SchoolId -> CourseName -> ExamName -> Handler Html +getEEGradesR = postEEGradesR +postEEGradesR = error "Not implemented" diff --git a/src/Handler/ExternalExam.hs b/src/Handler/ExternalExam.hs new file mode 100644 index 000000000..a715bcd8f --- /dev/null +++ b/src/Handler/ExternalExam.hs @@ -0,0 +1,9 @@ +module Handler.ExternalExam + ( module Handler.ExternalExam + ) where + +import Handler.ExternalExam.List as Handler.ExternalExam +import Handler.ExternalExam.New as Handler.ExternalExam +import Handler.ExternalExam.Show as Handler.ExternalExam +import Handler.ExternalExam.Edit as Handler.ExternalExam +import Handler.ExternalExam.Users as Handler.ExternalExam diff --git a/src/Handler/ExternalExam/Edit.hs b/src/Handler/ExternalExam/Edit.hs new file mode 100644 index 000000000..66961d477 --- /dev/null +++ b/src/Handler/ExternalExam/Edit.hs @@ -0,0 +1,10 @@ +module Handler.ExternalExam.Edit + ( getEEEditR, postEEEditR + ) where + +import Import + + +getEEEditR, postEEEditR :: TermId -> SchoolId -> CourseName -> ExamName -> Handler Html +getEEEditR = postEEEditR +postEEEditR = error "Not implemented" diff --git a/src/Handler/ExternalExam/List.hs b/src/Handler/ExternalExam/List.hs new file mode 100644 index 000000000..7ecde480c --- /dev/null +++ b/src/Handler/ExternalExam/List.hs @@ -0,0 +1,9 @@ +module Handler.ExternalExam.List + ( getEExamListR + ) where + +import Import + + +getEExamListR :: Handler Html +getEExamListR = error "Not implemented" diff --git a/src/Handler/ExternalExam/New.hs b/src/Handler/ExternalExam/New.hs new file mode 100644 index 000000000..1ccd89731 --- /dev/null +++ b/src/Handler/ExternalExam/New.hs @@ -0,0 +1,10 @@ +module Handler.ExternalExam.New + ( getEExamNewR, postEExamNewR + ) where + +import Import + + +getEExamNewR, postEExamNewR :: Handler Html +getEExamNewR = postEExamNewR +postEExamNewR = error "Not implemented" diff --git a/src/Handler/ExternalExam/Show.hs b/src/Handler/ExternalExam/Show.hs new file mode 100644 index 000000000..8189dcc5a --- /dev/null +++ b/src/Handler/ExternalExam/Show.hs @@ -0,0 +1,9 @@ +module Handler.ExternalExam.Show + ( getEEShowR + ) where + +import Import + + +getEEShowR :: TermId -> SchoolId -> CourseName -> ExamName -> Handler Html +getEEShowR = error "Not implemented" diff --git a/src/Handler/ExternalExam/Users.hs b/src/Handler/ExternalExam/Users.hs new file mode 100644 index 000000000..8fc8eb3ef --- /dev/null +++ b/src/Handler/ExternalExam/Users.hs @@ -0,0 +1,10 @@ +module Handler.ExternalExam.Users + ( getEEUsersR, postEEUsersR + ) where + +import Import + + +getEEUsersR, postEEUsersR :: TermId -> SchoolId -> CourseName -> ExamName -> Handler Html +getEEUsersR = postEEUsersR +postEEUsersR = error "Not implemented" diff --git a/src/Handler/Utils/ExamOffice/ExternalExam.hs b/src/Handler/Utils/ExamOffice/ExternalExam.hs new file mode 100644 index 000000000..ec32091d9 --- /dev/null +++ b/src/Handler/Utils/ExamOffice/ExternalExam.hs @@ -0,0 +1,39 @@ +module Handler.Utils.ExamOffice.ExternalExam + ( examOfficeExternalExamResultAuth + ) where + +import Import.NoFoundation + +import qualified Database.Esqueleto as E + + +examOfficeExternalExamResultAuth :: E.SqlExpr (E.Value UserId) -- ^ office + -> E.SqlExpr (Entity ExternalExamResult) + -> E.SqlExpr (E.Value Bool) +examOfficeExternalExamResultAuth authId eexamResult = authByUser E.||. authByField E.||. authBySchool E.||. authByExtraSchool + where + authByField = E.exists . E.from $ \(examOfficeField `E.InnerJoin` studyFeatures) -> do + E.on $ studyFeatures E.^. StudyFeaturesField E.==. examOfficeField E.^. ExamOfficeFieldField + E.where_ $ studyFeatures E.^. StudyFeaturesUser E.==. eexamResult E.^. ExternalExamResultUser + E.&&. examOfficeField E.^. ExamOfficeFieldOffice E.==. authId + E.&&. examOfficeField E.^. ExamOfficeFieldField E.==. studyFeatures E.^. StudyFeaturesField + E.where_ $ examOfficeField E.^. ExamOfficeFieldForced + E.||. E.exists (E.from $ \userFunction -> + E.where_ $ userFunction E.^. UserFunctionUser E.==. authId + E.&&. userFunction E.^. UserFunctionFunction E.==. E.val SchoolExamOffice + ) + + authByUser = E.exists . E.from $ \examOfficeUser -> + E.where_ $ examOfficeUser E.^. ExamOfficeUserOffice E.==. authId + E.&&. examOfficeUser E.^. ExamOfficeUserUser E.==. eexamResult E.^. ExternalExamResultUser + + authBySchool = E.exists . E.from $ \(userFunction `E.InnerJoin` eexam) -> do + E.on $ userFunction E.^. UserFunctionFunction E.==. E.val SchoolExamOffice + E.&&. userFunction E.^. UserFunctionSchool E.==. eexam E.^. ExternalExamSchool + E.where_ $ eexam E.^. ExternalExamId E.==. eexamResult E.^. ExternalExamResultExam + E.where_ $ userFunction E.^. UserFunctionUser E.==. authId + authByExtraSchool = E.exists . E.from $ \(userFunction `E.InnerJoin` eexamSchool) -> do + E.on $ userFunction E.^. UserFunctionFunction E.==. E.val SchoolExamOffice + E.&&. userFunction E.^. UserFunctionSchool E.==. eexamSchool E.^. ExternalExamOfficeSchoolSchool + E.where_ $ eexamSchool E.^. ExternalExamOfficeSchoolExam E.==. eexamResult E.^. ExternalExamResultExam + E.where_ $ userFunction E.^. UserFunctionUser E.==. authId diff --git a/templates/widgets/breadcrumbs/breadcrumbs.hamlet b/templates/widgets/breadcrumbs/breadcrumbs.hamlet index 50d0e1844..c6aa3cc4f 100644 --- a/templates/widgets/breadcrumbs/breadcrumbs.hamlet +++ b/templates/widgets/breadcrumbs/breadcrumbs.hamlet @@ -1,7 +1,10 @@ $newline never