feat(tutorials): delegate control to tutors

This commit is contained in:
Gregor Kleen 2019-10-14 11:50:06 +02:00
parent 31524132ca
commit 261f3ed92f
15 changed files with 53 additions and 11 deletions

View File

@ -412,6 +412,7 @@ UnknownAuthPredicate tag@String: Authorisierungsprädikat "#{tag}" ist dem Syste
UnauthorizedRedirect: Die angeforderte Seite existiert nicht oder Sie haben keine Berechtigung, die angeforderte Seite zu sehen. UnauthorizedRedirect: Die angeforderte Seite existiert nicht oder Sie haben keine Berechtigung, die angeforderte Seite zu sehen.
UnauthorizedSelf: Aktueller Nutzer ist nicht angegebener Benutzer. UnauthorizedSelf: Aktueller Nutzer ist nicht angegebener Benutzer.
UnauthorizedTutorialTutor: Sie sind nicht Tutor für dieses Tutorium. UnauthorizedTutorialTutor: Sie sind nicht Tutor für dieses Tutorium.
UnauthorizedTutorialTutorControl: Tutoren dürfen dieses Tutorium nicht editieren.
UnauthorizedCourseTutor: Sie sind nicht Tutor für diesen Kurs. UnauthorizedCourseTutor: Sie sind nicht Tutor für diesen Kurs.
UnauthorizedTutor: Sie sind nicht Tutor. UnauthorizedTutor: Sie sind nicht Tutor.
UnauthorizedTutorialRegisterGroup: Sie sind bereits in einem Tutorium mit derselben Registrierungs-Gruppe. UnauthorizedTutorialRegisterGroup: Sie sind bereits in einem Tutorium mit derselben Registrierungs-Gruppe.
@ -1126,6 +1127,7 @@ AuthTagDevelopment: Seite ist nicht in Entwicklung
AuthTagLecturer: Nutzer ist Dozent AuthTagLecturer: Nutzer ist Dozent
AuthTagCorrector: Nutzer ist Korrektor AuthTagCorrector: Nutzer ist Korrektor
AuthTagTutor: Nutzer ist Tutor AuthTagTutor: Nutzer ist Tutor
AuthTagTutorControl: Tutoren haben Kontrolle über ihre Tutorium
AuthTagTime: Zeitliche Einschränkungen sind erfüllt AuthTagTime: Zeitliche Einschränkungen sind erfüllt
AuthTagStaffTime: Zeitliche Einschränkungen für Lehrbeteiligte sind erfüllt AuthTagStaffTime: Zeitliche Einschränkungen für Lehrbeteiligte sind erfüllt
AuthTagAllocationTime: Zeitliche Einschränkungen durch Zentralanmeldung sind erfüllt AuthTagAllocationTime: Zeitliche Einschränkungen durch Zentralanmeldung sind erfüllt
@ -1276,6 +1278,8 @@ TutorialDeregisterUntil: Abmeldungen bis
TutorialsHeading: Tutorien TutorialsHeading: Tutorien
TutorialEdit: Bearbeiten TutorialEdit: Bearbeiten
TutorialDelete: Löschen TutorialDelete: Löschen
TutorialTutorControlled: Tutoren dürfen Tutorium editieren
TutorialTutorControlledTip: Sollen Tutoren beliebige Aspekte dieses Tutoriums (Name, Registrierungs-Gruppe, Raum, Zeit, andere Tutoren, ...) beliebig editieren dürfen?
CourseExams: Prüfungen CourseExams: Prüfungen
CourseTutorials: Übungen CourseTutorials: Übungen

View File

@ -10,6 +10,7 @@ Tutorial json
registerTo UTCTime Maybe registerTo UTCTime Maybe
deregisterUntil UTCTime Maybe deregisterUntil UTCTime Maybe
lastChanged UTCTime default=now() lastChanged UTCTime default=now()
tutorControlled Bool default=false
UniqueTutorial course name UniqueTutorial course name
deriving Generic deriving Generic
Tutor Tutor

4
routes
View File

@ -158,12 +158,12 @@
/tuts CTutorialListR GET !tutor -- THIS route is used to check for overall course tutor access! /tuts CTutorialListR GET !tutor -- THIS route is used to check for overall course tutor access!
/tuts/new CTutorialNewR GET POST /tuts/new CTutorialNewR GET POST
/tuts/#TutorialName TutorialR: /tuts/#TutorialName TutorialR:
/edit TEditR GET POST /edit TEditR GET POST !tutorANDtutor-control
/delete TDeleteR GET POST /delete TDeleteR GET POST
/participants TUsersR GET POST !tutor /participants TUsersR GET POST !tutor
/register TRegisterR POST !timeANDcapacityANDcourse-registeredANDregister-group !timeANDtutorial-registered /register TRegisterR POST !timeANDcapacityANDcourse-registeredANDregister-group !timeANDtutorial-registered
/communication TCommR GET POST !tutor /communication TCommR GET POST !tutor
/tutor-invite TInviteR GET POST /tutor-invite TInviteR GET POST !tutorANDtutor-control
/exams CExamListR GET !free /exams CExamListR GET !free
/exams/new CExamNewR GET POST /exams/new CExamNewR GET POST
/exams/#ExamName ExamR: /exams/#ExamName ExamR:

View File

@ -132,6 +132,12 @@ data Transaction
{ transactionOffice :: UserId { transactionOffice :: UserId
, transactionField :: StudyTermsId , transactionField :: StudyTermsId
} }
| TransactionTutorialEdit
{ transactionTutorial :: TutorialId
}
| TransactionTutorialDelete
{ transactionTutorial :: TutorialId
}
deriving (Eq, Ord, Read, Show, Generic, Typeable) deriving (Eq, Ord, Read, Show, Generic, Typeable)

View File

@ -799,6 +799,13 @@ tagAccessPredicate AuthTutor = APDB $ \mAuthId route _ -> exceptT return return
_ -> do _ -> do
guardMExceptT (not $ Map.null resMap) (unauthorizedI MsgUnauthorizedTutor) guardMExceptT (not $ Map.null resMap) (unauthorizedI MsgUnauthorizedTutor)
return Authorized return Authorized
tagAccessPredicate AuthTutorControl = APDB $ \_ route _ -> case route of
CTutorialR tid ssh csh tutn _ -> maybeT (unauthorizedI MsgUnauthorizedTutorialTutorControl) $ do
Entity cid _ <- $cachedHereBinary (tid, ssh, csh) . MaybeT . getBy $ TermSchoolCourseShort tid ssh csh
Entity _ Tutorial{..} <- $cachedHereBinary (cid, tutn) . MaybeT . getBy $ UniqueTutorial cid tutn
guard tutorialTutorControlled
return Authorized
r -> $unsupportedAuthPredicate AuthTutorControl r
tagAccessPredicate AuthTime = APDB $ \mAuthId route _ -> case route of tagAccessPredicate AuthTime = APDB $ \mAuthId route _ -> case route of
CApplicationR tid ssh csh _ _ -> maybeT (unauthorizedI MsgUnauthorizedApplicationTime) $ do CApplicationR tid ssh csh _ _ -> maybeT (unauthorizedI MsgUnauthorizedApplicationTime) $ do
course <- $cachedHereBinary (tid, ssh, csh) . MaybeT . getKeyBy $ TermSchoolCourseShort tid ssh csh course <- $cachedHereBinary (tid, ssh, csh) . MaybeT . getKeyBy $ TermSchoolCourseShort tid ssh csh

View File

@ -54,7 +54,7 @@ getInfoLecturerR =
tooltipPlanned = [whamlet| _{MsgLecturerInfoTooltipPlanned} |] tooltipPlanned = [whamlet| _{MsgLecturerInfoTooltipPlanned} |]
tooltipNewU2W = [whamlet| _{MsgLecturerInfoTooltipNewU2W} |] tooltipNewU2W = [whamlet| _{MsgLecturerInfoTooltipNewU2W} |]
newU2WFeat, probFeatInline, plannedFeat, plannedFeatInline :: WidgetFor UniWorX () newU2WFeat, probFeatInline, plannedFeat, plannedFeatInline :: WidgetFor UniWorX ()
newU2WFeat = [whamlet| ^{iconTooltip tooltipNew (Just IconAnnounce) True} |] -- to be used inside text blocks newU2WFeat = [whamlet| ^{iconTooltip tooltipNewU2W (Just IconAnnounce) True} |] -- to be used inside text blocks
probFeatInline = [whamlet| ^{iconTooltip tooltipProblem (Just IconProblem) True} |] -- to be used inside text blocks probFeatInline = [whamlet| ^{iconTooltip tooltipProblem (Just IconProblem) True} |] -- to be used inside text blocks
plannedFeat = [whamlet| ^{iconTooltip tooltipPlanned (Just IconPlanned) False} |] plannedFeat = [whamlet| ^{iconTooltip tooltipPlanned (Just IconPlanned) False} |]
plannedFeatInline = [whamlet| ^{iconTooltip tooltipPlanned (Just IconPlanned) True} |] -- to be used inside text blocks plannedFeatInline = [whamlet| ^{iconTooltip tooltipPlanned (Just IconPlanned) True} |] -- to be used inside text blocks
@ -66,4 +66,4 @@ getInfoLecturerR =
let expiryTime = UTCTime (addGregorianMonthsRollOver 1 $ fromGregorian year month day) 0 let expiryTime = UTCTime (addGregorianMonthsRollOver 1 $ fromGregorian year month day) 0
if currentTime > expiryTime if currentTime > expiryTime
then mempty then mempty
else toWidget [whamlet| ^{iconTooltip tooltipNewU2W (Just IconNew) False} |] else toWidget [whamlet| ^{iconTooltip tooltipNew (Just IconNew) False} |]

View File

@ -35,5 +35,5 @@ postTDeleteR tid ssh csh tutn = do
, drSuccessMessage = SomeMessage MsgTutorialDeleted , drSuccessMessage = SomeMessage MsgTutorialDeleted
, drAbort = SomeRoute $ CTutorialR tid ssh csh tutn TUsersR , drAbort = SomeRoute $ CTutorialR tid ssh csh tutn TUsersR
, drSuccess = SomeRoute $ CourseR tid ssh csh CTutorialListR , drSuccess = SomeRoute $ CourseR tid ssh csh CTutorialListR
, drDelete = const id -- TODO: audit , drDelete = \tutid' act -> act <* audit (TransactionTutorialDelete tutid')
} }

View File

@ -42,6 +42,7 @@ postTEditR tid ssh csh tutn = do
, tfDeregisterUntil = tutorialDeregisterUntil , tfDeregisterUntil = tutorialDeregisterUntil
, tfTutors = Set.fromList (map Right tutorIds) , tfTutors = Set.fromList (map Right tutorIds)
<> Set.mapMonotonic Left (Map.keysSet tutorInvites) <> Set.mapMonotonic Left (Map.keysSet tutorInvites)
, tfTutorControlled = tutorialTutorControlled
} }
return (cid, tutid, template) return (cid, tutid, template)
@ -63,8 +64,11 @@ postTEditR tid ssh csh tutn = do
, tutorialRegisterTo = tfRegisterTo , tutorialRegisterTo = tfRegisterTo
, tutorialDeregisterUntil = tfDeregisterUntil , tutorialDeregisterUntil = tfDeregisterUntil
, tutorialLastChanged = now , tutorialLastChanged = now
, tutorialTutorControlled = tfTutorControlled
} }
when (is _Nothing insertRes) $ do when (is _Nothing insertRes) $ do
audit $ TransactionTutorialEdit tutid
let (invites, adds) = partitionEithers $ Set.toList tfTutors let (invites, adds) = partitionEithers $ Set.toList tfTutors
deleteWhere [ TutorTutorial ==. tutid ] deleteWhere [ TutorTutorial ==. tutid ]

View File

@ -18,10 +18,11 @@ import qualified Data.CaseInsensitive as CI
data TutorialForm = TutorialForm data TutorialForm = TutorialForm
{ tfName :: TutorialName { tfName :: TutorialName
, tfType :: CI Text , tfType :: CI Text
, tfRegGroup :: Maybe (CI Text)
, tfTutorControlled :: Bool
, tfCapacity :: Maybe Int , tfCapacity :: Maybe Int
, tfRoom :: Text , tfRoom :: Text
, tfTime :: Occurrences , tfTime :: Occurrences
, tfRegGroup :: Maybe (CI Text)
, tfRegisterFrom :: Maybe UTCTime , tfRegisterFrom :: Maybe UTCTime
, tfRegisterTo :: Maybe UTCTime , tfRegisterTo :: Maybe UTCTime
, tfDeregisterUntil :: Maybe UTCTime , tfDeregisterUntil :: Maybe UTCTime
@ -66,10 +67,11 @@ tutorialForm cid template html = do
flip (renderAForm FormStandard) html $ TutorialForm flip (renderAForm FormStandard) html $ TutorialForm
<$> areq (textField & cfStrip & cfCI) (fslpI MsgTutorialName (mr MsgTutorialName) & setTooltip MsgTutorialNameTip) (tfName <$> template) <$> areq (textField & cfStrip & cfCI) (fslpI MsgTutorialName (mr MsgTutorialName) & setTooltip MsgTutorialNameTip) (tfName <$> template)
<*> areq (textField & cfStrip & cfCI & addDatalist tutTypeDatalist) (fslpI MsgTutorialType $ mr MsgTutorialType) (tfType <$> template) <*> areq (textField & cfStrip & cfCI & addDatalist tutTypeDatalist) (fslpI MsgTutorialType $ mr MsgTutorialType) (tfType <$> template)
<*> aopt (textField & cfStrip & cfCI) (fslI MsgTutorialRegGroup & setTooltip MsgTutorialRegGroupTip) ((tfRegGroup <$> template) <|> Just (Just "tutorial"))
<*> apopt checkBoxField (fslI MsgTutorialTutorControlled & setTooltip MsgTutorialTutorControlledTip) (tfTutorControlled <$> template)
<*> aopt (natFieldI MsgTutorialCapacityNonPositive) (fslpI MsgTutorialCapacity (mr MsgTutorialCapacity) & setTooltip MsgTutorialCapacityTip) (tfCapacity <$> template) <*> aopt (natFieldI MsgTutorialCapacityNonPositive) (fslpI MsgTutorialCapacity (mr MsgTutorialCapacity) & setTooltip MsgTutorialCapacityTip) (tfCapacity <$> template)
<*> areq textField (fslpI MsgTutorialRoom $ mr MsgTutorialRoomPlaceholder) (tfRoom <$> template) <*> areq textField (fslpI MsgTutorialRoom $ mr MsgTutorialRoomPlaceholder) (tfRoom <$> template)
<*> occurrencesAForm ("occurrences" :: Text) (tfTime <$> template) <*> occurrencesAForm ("occurrences" :: Text) (tfTime <$> template)
<*> aopt (textField & cfStrip & cfCI) (fslI MsgTutorialRegGroup & setTooltip MsgTutorialRegGroupTip) ((tfRegGroup <$> template) <|> Just (Just "tutorial"))
<*> aopt utcTimeField (fslpI MsgRegisterFrom (mr MsgDate) <*> aopt utcTimeField (fslpI MsgRegisterFrom (mr MsgDate)
& setTooltip MsgCourseRegisterFromTip & setTooltip MsgCourseRegisterFromTip
) (tfRegisterFrom <$> template) ) (tfRegisterFrom <$> template)

View File

@ -35,8 +35,11 @@ postCTutorialNewR tid ssh csh = do
, tutorialRegisterTo = tfRegisterTo , tutorialRegisterTo = tfRegisterTo
, tutorialDeregisterUntil = tfDeregisterUntil , tutorialDeregisterUntil = tfDeregisterUntil
, tutorialLastChanged = now , tutorialLastChanged = now
, tutorialTutorControlled = tfTutorControlled
} }
whenIsJust insertRes $ \tutid -> do whenIsJust insertRes $ \tutid -> do
audit $ TransactionTutorialEdit tutid
let (invites, adds) = partitionEithers $ Set.toList tfTutors let (invites, adds) = partitionEithers $ Set.toList tfTutors
insertMany_ $ map (Tutor tutid) adds insertMany_ $ map (Tutor tutid) adds
sinkInvitationsF tutorInvitationConfig $ map (, tutid, (InvDBDataTutor, InvTokenDataTutor)) invites sinkInvitationsF tutorInvitationConfig $ map (, tutid, (InvDBDataTutor, InvTokenDataTutor)) invites

View File

@ -43,6 +43,7 @@ data AuthTag -- sortiert nach gewünschter Reihenfolge auf /authpreds, d.h. Prä
| AuthLecturer | AuthLecturer
| AuthCorrector | AuthCorrector
| AuthTutor | AuthTutor
| AuthTutorControl
| AuthExamOffice | AuthExamOffice
| AuthAllocationRegistered | AuthAllocationRegistered
| AuthCourseRegistered | AuthCourseRegistered

View File

@ -1,5 +1,11 @@
$newline never $newline never
<dl .deflist> <dl .deflist>
<dt .deflist__dt>
^{formatGregorianW 2019 10 14}
<dd .deflist__dd>
<ul>
<li>Kontrolle über Einstellungen eines Tutoriums kann an Tutoren deligiert werden
<dt .deflist__dt> <dt .deflist__dt>
^{formatGregorianW 2019 10 10} ^{formatGregorianW 2019 10 10}
<dd .deflist__dd> <dd .deflist__dd>

View File

@ -254,7 +254,13 @@ $newline text
Eine Tutoriumsgruppe kann beliebig viele Tutoren haben und ein Tutor kann beliebig viele Tutoriengruppen betreuen. Eine Tutoriumsgruppe kann beliebig viele Tutoren haben und ein Tutor kann beliebig viele Tutoriengruppen betreuen.
<p> <p>
Tutoren haben Zugriff auf die Namen und Studiendaten ihrer Tutoriums-Teilnehmer und können auch Mitteilungen an sie verschicken (analog zu Kursmitteilungen). Tutoren haben Zugriff auf die Namen und Studiendaten ihrer Tutoriums-Teilnehmer, können Mitteilungen an sie verschicken (analog zu Kursmitteilungen) und Teilnehmer aus ihrem Tutorium entfernen.
<p>
^{newFeat 2019 10 14} Optional kann den Tutoren volle Kontrolle über
ihre Tutorien überlassen werden (bis auf Löschen des Tutoriums), Tutoren
können dann insbesondere eigenständig den regulären Raum und die Zeit
ihres Tutoriums anpassen.
<dt .deflist__dt> Anmeldung <dt .deflist__dt> Anmeldung
<dd .deflist__dd> <dd .deflist__dd>
@ -277,10 +283,9 @@ $newline text
<p> <p>
Um die Anmeldung in beliebig viele Tutoriumsgruppen zuzulassen können alle Registrierungs-Gruppen leer gelassen werden. Um die Anmeldung in beliebig viele Tutoriumsgruppen zuzulassen können alle Registrierungs-Gruppen leer gelassen werden.
<dt .deflist__dt> ^{plannedFeat} Nachmeldung <dt .deflist__dt> ^{newFeat 2019 10 10} Nachmeldung
<dt .deflist__dd> <dt .deflist__dd>
<p> Kursverwalter können über die Teilnehmerliste der Veranstaltung Kursteilnehmer in Tutorien einteilen.
Es gibt zur Zeit keine Möglichkeit für die Kursverwalter oder Tutoren, Teilnehmer zu einem Tutorium hinzuzufügen.
<section id="exams"> <section id="exams">

View File

@ -716,6 +716,7 @@ fillDb = do
, tutorialRegisterTo = Nothing , tutorialRegisterTo = Nothing
, tutorialDeregisterUntil = Nothing , tutorialDeregisterUntil = Nothing
, tutorialLastChanged = now , tutorialLastChanged = now
, tutorialTutorControlled = True
} }
void . insert $ Tutor tut1 gkleen void . insert $ Tutor tut1 gkleen
void . insert $ TutorialParticipant tut1 fhamann void . insert $ TutorialParticipant tut1 fhamann
@ -734,6 +735,7 @@ fillDb = do
, tutorialRegisterTo = Nothing , tutorialRegisterTo = Nothing
, tutorialDeregisterUntil = Nothing , tutorialDeregisterUntil = Nothing
, tutorialLastChanged = now , tutorialLastChanged = now
, tutorialTutorControlled = False
} }
void . insert $ Tutor tut2 gkleen void . insert $ Tutor tut2 gkleen
-- datenbanksysteme -- datenbanksysteme

View File

@ -70,6 +70,7 @@ instance Arbitrary Tutorial where
<*> arbitrary <*> arbitrary
<*> arbitrary <*> arbitrary
<*> arbitrary <*> arbitrary
<*> arbitrary
shrink = genericShrink shrink = genericShrink
instance Arbitrary User where instance Arbitrary User where