Merge branch 'master' into pageactions
This commit is contained in:
commit
8110405534
@ -1,3 +1,7 @@
|
|||||||
|
* Version 30.01.2019
|
||||||
|
|
||||||
|
Designänderungen
|
||||||
|
|
||||||
* Version 16.01.2019
|
* Version 16.01.2019
|
||||||
|
|
||||||
Links für Bequemlichkeiten hinzugefügt (z.B. aktuelles Übungsblatt)
|
Links für Bequemlichkeiten hinzugefügt (z.B. aktuelles Übungsblatt)
|
||||||
|
|||||||
@ -7,6 +7,7 @@ BtnHijack: Sitzung übernehmen
|
|||||||
|
|
||||||
Aborted: Abgebrochen
|
Aborted: Abgebrochen
|
||||||
Registered: Angemeldet
|
Registered: Angemeldet
|
||||||
|
RegisteredSince date@Text: Angemeldet seit #{date}
|
||||||
RegisterFrom: Anmeldungen von
|
RegisterFrom: Anmeldungen von
|
||||||
RegisterTo: Anmeldungen bis
|
RegisterTo: Anmeldungen bis
|
||||||
DeRegUntil: Abmeldungen bis
|
DeRegUntil: Abmeldungen bis
|
||||||
@ -69,7 +70,7 @@ CourseSemester: Semester
|
|||||||
CourseSchool: Institut
|
CourseSchool: Institut
|
||||||
CourseSchoolShort: Fach
|
CourseSchoolShort: Fach
|
||||||
CourseSecretTip: Anmeldung zum Kurs erfordert Eingabe des Passworts, sofern gesetzt
|
CourseSecretTip: Anmeldung zum Kurs erfordert Eingabe des Passworts, sofern gesetzt
|
||||||
CourseRegisterFromTip: Ohne Datum ist keine eigenständige Anmeldung von Studierenden möglich
|
CourseRegisterFromTip: Ohne Datum ist KEINE eigenständige Anmeldung von Studierenden möglich
|
||||||
CourseRegisterToTip: Anmeldung darf auch ohne Begrenzung möglich sein
|
CourseRegisterToTip: Anmeldung darf auch ohne Begrenzung möglich sein
|
||||||
CourseDeregisterUntilTip: Abmeldung darf auch ohne Begrenzung möglich sein
|
CourseDeregisterUntilTip: Abmeldung darf auch ohne Begrenzung möglich sein
|
||||||
CourseFilterSearch: Volltext-Suche
|
CourseFilterSearch: Volltext-Suche
|
||||||
@ -108,7 +109,7 @@ SheetSolutionFrom: Lösung ab
|
|||||||
SheetMarking: Hinweise für Korrektoren
|
SheetMarking: Hinweise für Korrektoren
|
||||||
SheetType: Wertung
|
SheetType: Wertung
|
||||||
SheetInvisible: Dieses Übungsblatt ist für Teilnehmer momentan unsichtbar!
|
SheetInvisible: Dieses Übungsblatt ist für Teilnehmer momentan unsichtbar!
|
||||||
SheetInvisibleUntil mFrom@Text: Dieses Übungsblatt ist für Teilnehmer momentan unsichtbar bis #{mFrom}!
|
SheetInvisibleUntil date@Text: Dieses Übungsblatt ist für Teilnehmer momentan unsichtbar bis #{date}!
|
||||||
SheetName: Name
|
SheetName: Name
|
||||||
SheetDescription: Hinweise für Teilnehmer
|
SheetDescription: Hinweise für Teilnehmer
|
||||||
SheetGroup: Gruppenabgabe
|
SheetGroup: Gruppenabgabe
|
||||||
@ -207,6 +208,7 @@ CorByProportionOnly proportion@Rational: #{display proportion} Anteile
|
|||||||
CorByProportionIncludingTutorial proportion@Rational: #{display proportion} Anteile - Tutorium
|
CorByProportionIncludingTutorial proportion@Rational: #{display proportion} Anteile - Tutorium
|
||||||
CorByProportionExcludingTutorial proportion@Rational: #{display proportion} Anteile + Tutorium
|
CorByProportionExcludingTutorial proportion@Rational: #{display proportion} Anteile + Tutorium
|
||||||
|
|
||||||
|
RowCount count@Int64: #{display count} #{pluralDE count "Eintrag" "Einträge"} insgesamt
|
||||||
DeleteRow: Zeile entfernen
|
DeleteRow: Zeile entfernen
|
||||||
ProportionNegative: Anteile dürfen nicht negativ sein
|
ProportionNegative: Anteile dürfen nicht negativ sein
|
||||||
CorrectorUpdated: Korrektor erfolgreich aktualisiert
|
CorrectorUpdated: Korrektor erfolgreich aktualisiert
|
||||||
@ -240,7 +242,7 @@ MultiFileUploadInfo: (Mehrere Dateien mit Shift oder Strg auswählen)
|
|||||||
|
|
||||||
NrColumn: Nr
|
NrColumn: Nr
|
||||||
SelectColumn: Auswahl
|
SelectColumn: Auswahl
|
||||||
DBTablePagesize: Einträge
|
DBTablePagesize: Einträge pro Seite
|
||||||
DBTablePagesizeAll: Alle
|
DBTablePagesizeAll: Alle
|
||||||
|
|
||||||
CorrDownload: Herunterladen
|
CorrDownload: Herunterladen
|
||||||
@ -568,15 +570,16 @@ MenuSubmissions: Abgaben
|
|||||||
MenuSheetList: Übungsblätter
|
MenuSheetList: Übungsblätter
|
||||||
MenuSheetNew: Neues Übungsblatt anlegen
|
MenuSheetNew: Neues Übungsblatt anlegen
|
||||||
MenuSheetCurrent: Aktuelles Übungsblatt
|
MenuSheetCurrent: Aktuelles Übungsblatt
|
||||||
MenuSheetLastInactive: Zuletzt abgegebenes Übungsblatt
|
MenuSheetOldUnassigned: Abgaben ohne Korrektor
|
||||||
MenuCourseEdit: Kurs editieren
|
MenuCourseEdit: Kurs editieren
|
||||||
MenuCourseNewTemplate: Als neuen Kurs klonen
|
MenuCourseClone: Als neuen Kurs klonen
|
||||||
MenuCourseDelete: Kurs löschen
|
MenuCourseDelete: Kurs löschen
|
||||||
MenuSubmissionNew: Abgabe anlegen
|
MenuSubmissionNew: Abgabe anlegen
|
||||||
MenuSubmissionOwn: Abgabe
|
MenuSubmissionOwn: Abgabe
|
||||||
MenuCorrectors: Korrektoren
|
MenuCorrectors: Korrektoren
|
||||||
MenuSheetEdit: Übungsblatt editieren
|
MenuSheetEdit: Übungsblatt editieren
|
||||||
MenuSheetDelete: Übungsblatt löschen
|
MenuSheetDelete: Übungsblatt löschen
|
||||||
|
MenuSheetClone: Als neues Übungsblatt klonen
|
||||||
MenuCorrectionsUpload: Korrekturen hochladen
|
MenuCorrectionsUpload: Korrekturen hochladen
|
||||||
MenuCorrectionsCreate: Abgaben registrieren
|
MenuCorrectionsCreate: Abgaben registrieren
|
||||||
MenuCorrectionsGrade: Abgaben bewerten
|
MenuCorrectionsGrade: Abgaben bewerten
|
||||||
@ -608,4 +611,4 @@ DeleteCopyStringIfSure n@Int: Wenn Sie sich sicher sind, dass Sie #{pluralDE n "
|
|||||||
DeleteConfirmation: Bestätigung
|
DeleteConfirmation: Bestätigung
|
||||||
DeleteConfirmationWrong: Bestätigung muss genau dem angezeigten Text entsprechen.
|
DeleteConfirmationWrong: Bestätigung muss genau dem angezeigten Text entsprechen.
|
||||||
|
|
||||||
DBTIRowsMissing n@Int: #{pluralDE n "Eine Zeile ist" "Einige Zeile sind"} aus der Datenbank verschwunden, seit das Formular für Sie generiert wurde
|
DBTIRowsMissing n@Int: #{pluralDE n "Eine Zeile ist" "Einige Zeile sind"} aus der Datenbank verschwunden, seit das Formular für Sie generiert wurde
|
||||||
|
|||||||
6
routes
6
routes
@ -72,9 +72,9 @@
|
|||||||
/notes CNotesR GET POST !corrector
|
/notes CNotesR GET POST !corrector
|
||||||
/subs CCorrectionsR GET POST
|
/subs CCorrectionsR GET POST
|
||||||
/ex SheetListR GET !registered !materials !corrector
|
/ex SheetListR GET !registered !materials !corrector
|
||||||
!/ex/new SheetNewR GET POST
|
/ex/new SheetNewR GET POST
|
||||||
!/ex/current SheetCurrentR GET !free -- just a redirect
|
/ex/current SheetCurrentR GET !registered !materials !corrector
|
||||||
!/ex/lastinactive SheetLastInactiveR GET !free -- just a redirect
|
/ex/unassigned SheetOldUnassigned GET
|
||||||
/ex/#SheetName SheetR:
|
/ex/#SheetName SheetR:
|
||||||
/show SShowR GET !timeANDregistered !timeANDmaterials !corrector
|
/show SShowR GET !timeANDregistered !timeANDmaterials !corrector
|
||||||
/edit SEditR GET POST
|
/edit SEditR GET POST
|
||||||
|
|||||||
@ -14,15 +14,14 @@ import qualified Data.CaseInsensitive as CI
|
|||||||
|
|
||||||
data DummyMessage = MsgDummyIdent
|
data DummyMessage = MsgDummyIdent
|
||||||
| MsgDummyNoFormData
|
| MsgDummyNoFormData
|
||||||
|
deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic, Typeable)
|
||||||
|
|
||||||
|
|
||||||
dummyForm :: ( RenderMessage site FormMessage
|
dummyForm :: ( RenderMessage site FormMessage
|
||||||
, RenderMessage site DummyMessage
|
, RenderMessage site DummyMessage
|
||||||
, RenderMessage site ButtonMessage
|
|
||||||
, YesodPersist site
|
, YesodPersist site
|
||||||
, SqlBackendCanRead (YesodPersistBackend site)
|
, SqlBackendCanRead (YesodPersistBackend site)
|
||||||
, Button site SubmitButton
|
, Button site ButtonSubmit
|
||||||
, Show (ButtonCssClass site)
|
|
||||||
) => AForm (HandlerT site IO) (CI Text)
|
) => AForm (HandlerT site IO) (CI Text)
|
||||||
dummyForm = areq (selectField userList) (fslI MsgDummyIdent) Nothing
|
dummyForm = areq (selectField userList) (fslI MsgDummyIdent) Nothing
|
||||||
<* submitButton
|
<* submitButton
|
||||||
@ -35,9 +34,7 @@ dummyLogin :: ( YesodAuth site
|
|||||||
, SqlBackendCanRead (YesodPersistBackend site)
|
, SqlBackendCanRead (YesodPersistBackend site)
|
||||||
, RenderMessage site FormMessage
|
, RenderMessage site FormMessage
|
||||||
, RenderMessage site DummyMessage
|
, RenderMessage site DummyMessage
|
||||||
, RenderMessage site ButtonMessage
|
, Button site ButtonSubmit
|
||||||
, Button site SubmitButton
|
|
||||||
, Show (ButtonCssClass site)
|
|
||||||
) => AuthPlugin site
|
) => AuthPlugin site
|
||||||
dummyLogin = AuthPlugin{..}
|
dummyLogin = AuthPlugin{..}
|
||||||
where
|
where
|
||||||
|
|||||||
@ -28,13 +28,14 @@ import qualified Yesod.Auth.Message as Msg
|
|||||||
data CampusLogin = CampusLogin
|
data CampusLogin = CampusLogin
|
||||||
{ campusIdent :: CI Text
|
{ campusIdent :: CI Text
|
||||||
, campusPassword :: Text
|
, campusPassword :: Text
|
||||||
}
|
} deriving (Generic, Typeable)
|
||||||
|
|
||||||
data CampusMessage = MsgCampusIdentNote
|
data CampusMessage = MsgCampusIdentNote
|
||||||
| MsgCampusIdent
|
| MsgCampusIdent
|
||||||
| MsgCampusPassword
|
| MsgCampusPassword
|
||||||
| MsgCampusSubmit
|
| MsgCampusSubmit
|
||||||
| MsgCampusInvalidCredentials
|
| MsgCampusInvalidCredentials
|
||||||
|
deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic, Typeable)
|
||||||
|
|
||||||
|
|
||||||
findUser :: LdapConf -> Ldap -> Text -> [Ldap.Attr] -> IO [Ldap.SearchEntry]
|
findUser :: LdapConf -> Ldap -> Text -> [Ldap.Attr] -> IO [Ldap.SearchEntry]
|
||||||
@ -53,9 +54,7 @@ userPrincipalName = Ldap.Attr "userPrincipalName"
|
|||||||
|
|
||||||
campusForm :: ( RenderMessage site FormMessage
|
campusForm :: ( RenderMessage site FormMessage
|
||||||
, RenderMessage site CampusMessage
|
, RenderMessage site CampusMessage
|
||||||
, RenderMessage site ButtonMessage
|
, Button site ButtonSubmit
|
||||||
, Button site SubmitButton
|
|
||||||
, Show (ButtonCssClass site)
|
|
||||||
) => AForm (HandlerT site IO) CampusLogin
|
) => AForm (HandlerT site IO) CampusLogin
|
||||||
campusForm = CampusLogin
|
campusForm = CampusLogin
|
||||||
<$> areq ciField (fslpI MsgCampusIdent "user.name@campus.lmu.de" & setTooltip MsgCampusIdentNote) Nothing
|
<$> areq ciField (fslpI MsgCampusIdent "user.name@campus.lmu.de" & setTooltip MsgCampusIdentNote) Nothing
|
||||||
@ -66,9 +65,7 @@ campusLogin :: forall site.
|
|||||||
( YesodAuth site
|
( YesodAuth site
|
||||||
, RenderMessage site FormMessage
|
, RenderMessage site FormMessage
|
||||||
, RenderMessage site CampusMessage
|
, RenderMessage site CampusMessage
|
||||||
, RenderMessage site ButtonMessage
|
, Button site ButtonSubmit
|
||||||
, Button site SubmitButton
|
|
||||||
, Show (ButtonCssClass site)
|
|
||||||
) => LdapConf -> LdapPool -> AuthPlugin site
|
) => LdapConf -> LdapPool -> AuthPlugin site
|
||||||
campusLogin conf@LdapConf{..} pool = AuthPlugin{..}
|
campusLogin conf@LdapConf{..} pool = AuthPlugin{..}
|
||||||
where
|
where
|
||||||
@ -116,7 +113,7 @@ data CampusUserException = CampusUserLdapError LdapPoolError
|
|||||||
| CampusUserHostCannotConnect String [IOException]
|
| CampusUserHostCannotConnect String [IOException]
|
||||||
| CampusUserNoResult
|
| CampusUserNoResult
|
||||||
| CampusUserAmbiguous
|
| CampusUserAmbiguous
|
||||||
deriving (Show, Eq, Typeable)
|
deriving (Show, Eq, Generic, Typeable)
|
||||||
|
|
||||||
instance Exception CampusUserException
|
instance Exception CampusUserException
|
||||||
|
|
||||||
|
|||||||
@ -19,17 +19,16 @@ import qualified Yesod.Auth.Message as Msg
|
|||||||
data HashLogin = HashLogin
|
data HashLogin = HashLogin
|
||||||
{ hashIdent :: CI Text
|
{ hashIdent :: CI Text
|
||||||
, hashPassword :: Text
|
, hashPassword :: Text
|
||||||
}
|
} deriving (Generic, Typeable)
|
||||||
|
|
||||||
data PWHashMessage = MsgPWHashIdent
|
data PWHashMessage = MsgPWHashIdent
|
||||||
| MsgPWHashPassword
|
| MsgPWHashPassword
|
||||||
|
deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic, Typeable)
|
||||||
|
|
||||||
|
|
||||||
hashForm :: ( RenderMessage site FormMessage
|
hashForm :: ( RenderMessage site FormMessage
|
||||||
, RenderMessage site PWHashMessage
|
, RenderMessage site PWHashMessage
|
||||||
, RenderMessage site ButtonMessage
|
, Button site ButtonSubmit
|
||||||
, Button site SubmitButton
|
|
||||||
, Show (ButtonCssClass site)
|
|
||||||
) => AForm (HandlerT site IO) HashLogin
|
) => AForm (HandlerT site IO) HashLogin
|
||||||
hashForm = HashLogin
|
hashForm = HashLogin
|
||||||
<$> areq ciField (fslpI MsgPWHashIdent "Identifikation") Nothing
|
<$> areq ciField (fslpI MsgPWHashIdent "Identifikation") Nothing
|
||||||
@ -42,9 +41,7 @@ hashLogin :: ( YesodAuth site
|
|||||||
, SqlBackendCanRead (YesodPersistBackend site)
|
, SqlBackendCanRead (YesodPersistBackend site)
|
||||||
, RenderMessage site FormMessage
|
, RenderMessage site FormMessage
|
||||||
, RenderMessage site PWHashMessage
|
, RenderMessage site PWHashMessage
|
||||||
, RenderMessage site ButtonMessage
|
, Button site ButtonSubmit
|
||||||
, Button site SubmitButton
|
|
||||||
, Show (ButtonCssClass site)
|
|
||||||
) => PWHashAlgorithm -> AuthPlugin site
|
) => PWHashAlgorithm -> AuthPlugin site
|
||||||
hashLogin pwHashAlgo = AuthPlugin{..}
|
hashLogin pwHashAlgo = AuthPlugin{..}
|
||||||
where
|
where
|
||||||
|
|||||||
@ -63,6 +63,7 @@ import Handler.Utils.StudyFeatures
|
|||||||
import Handler.Utils.Templates
|
import Handler.Utils.Templates
|
||||||
import Utils.Lens
|
import Utils.Lens
|
||||||
import Utils.Form
|
import Utils.Form
|
||||||
|
import Utils.Sheet
|
||||||
import Utils.SystemMessage
|
import Utils.SystemMessage
|
||||||
|
|
||||||
import Text.Shakespeare.Text (st)
|
import Text.Shakespeare.Text (st)
|
||||||
@ -275,20 +276,35 @@ instance HasRoute UniWorX MenuItem where
|
|||||||
urlRoute MenuItem{..} = urlRoute menuItemRoute
|
urlRoute MenuItem{..} = urlRoute menuItemRoute
|
||||||
|
|
||||||
menuItemAccessCallback :: MenuItem -> Handler Bool
|
menuItemAccessCallback :: MenuItem -> Handler Bool
|
||||||
menuItemAccessCallback MenuItem{..} = (&&) <$> ((==) Authorized <$> authCheck) <*> menuItemAccessCallback'
|
menuItemAccessCallback MenuItem{..} = and2M ((==) Authorized <$> authCheck) menuItemAccessCallback'
|
||||||
where
|
where
|
||||||
authCheck = handleAny (\_ -> return . Unauthorized $ error "authCheck caught exception") $ isAuthorized (urlRoute menuItemRoute) False
|
authCheck = handleAny (\_ -> return . Unauthorized $ error "authCheck caught exception") $ isAuthorized (urlRoute menuItemRoute) False
|
||||||
|
|
||||||
$(return [])
|
$(return [])
|
||||||
|
|
||||||
|
|
||||||
data instance ButtonCssClass UniWorX = BCDefault | BCPrimary | BCSuccess | BCInfo | BCWarning | BCDanger | BCLink
|
data instance ButtonClass UniWorX
|
||||||
deriving (Enum, Eq, Ord, Bounded, Read, Show)
|
= BCIsButton
|
||||||
|
| BCDefault
|
||||||
|
| BCPrimary
|
||||||
|
| BCSuccess
|
||||||
|
| BCInfo
|
||||||
|
| BCWarning
|
||||||
|
| BCDanger
|
||||||
|
| BCLink
|
||||||
|
deriving (Enum, Eq, Ord, Bounded, Read, Show, Generic, Typeable)
|
||||||
|
instance Universe (ButtonClass UniWorX)
|
||||||
|
instance Finite (ButtonClass UniWorX)
|
||||||
|
|
||||||
instance Button UniWorX SubmitButton where
|
instance PathPiece (ButtonClass UniWorX) where
|
||||||
label BtnSubmit = [whamlet|_{MsgBtnSubmit}|]
|
toPathPiece BCIsButton = "btn"
|
||||||
|
toPathPiece bClass = ("btn-" <>) . camelToPathPiece' 1 $ tshow bClass
|
||||||
|
fromPathPiece = finiteFromPathPiece
|
||||||
|
|
||||||
cssClass BtnSubmit = BCPrimary
|
|
||||||
|
embedRenderMessage ''UniWorX ''ButtonSubmit id
|
||||||
|
instance Button UniWorX ButtonSubmit where
|
||||||
|
btnClasses BtnSubmit = [BCIsButton, BCPrimary]
|
||||||
|
|
||||||
|
|
||||||
getTimeLocale' :: [Lang] -> TimeLocale
|
getTimeLocale' :: [Lang] -> TimeLocale
|
||||||
@ -469,12 +485,23 @@ tagAccessPredicate AuthTime = APDB $ \route _ -> case route of
|
|||||||
|
|
||||||
return Authorized
|
return Authorized
|
||||||
|
|
||||||
CourseR tid ssh csh CRegisterR -> maybeT (unauthorizedI MsgUnauthorizedCourseTime) $ do
|
CourseR tid ssh csh CRegisterR -> do
|
||||||
Entity _ Course{courseRegisterFrom, courseRegisterTo} <- MaybeT . getBy $ TermSchoolCourseShort tid ssh csh
|
now <- liftIO getCurrentTime
|
||||||
cTime <- (NTop . Just) <$> liftIO getCurrentTime
|
mbc <- getBy $ TermSchoolCourseShort tid ssh csh
|
||||||
guard $ NTop courseRegisterFrom <= cTime
|
mAid <- lift maybeAuthId
|
||||||
&& NTop courseRegisterTo >= cTime
|
registered <- case (mbc,mAid) of
|
||||||
return Authorized
|
(Just (Entity cid _), Just uid) -> isJust <$> (getBy $ UniqueParticipant uid cid)
|
||||||
|
_ -> return False
|
||||||
|
case mbc of
|
||||||
|
(Just (Entity _ Course{courseRegisterFrom, courseRegisterTo}))
|
||||||
|
| not registered
|
||||||
|
, Just regFrom <- courseRegisterFrom -- Nothing = no registration
|
||||||
|
, regFrom <= now
|
||||||
|
, maybe True (now <=) courseRegisterTo -> return Authorized
|
||||||
|
(Just (Entity _ Course{courseDeregisterUntil}))
|
||||||
|
| registered
|
||||||
|
, maybe True (now <=) courseDeregisterUntil -> return Authorized
|
||||||
|
_other -> unauthorizedI MsgUnauthorizedCourseTime
|
||||||
|
|
||||||
MessageR cID -> maybeT (unauthorizedI MsgUnauthorizedSystemMessageTime) $ do
|
MessageR cID -> maybeT (unauthorizedI MsgUnauthorizedSystemMessageTime) $ do
|
||||||
smId <- decrypt cID
|
smId <- decrypt cID
|
||||||
@ -1269,8 +1296,8 @@ pageActions (CourseR tid ssh csh CShowR) =
|
|||||||
}
|
}
|
||||||
, MenuItem
|
, MenuItem
|
||||||
{ menuItemType = PageActionSecondary
|
{ menuItemType = PageActionSecondary
|
||||||
, menuItemLabel = MsgMenuCourseNewTemplate
|
, menuItemLabel = MsgMenuCourseClone
|
||||||
, menuItemIcon = Nothing
|
, menuItemIcon = Just "copy"
|
||||||
, menuItemRoute = SomeRoute (CourseNewR, [("tid", toPathPiece tid), ("ssh", toPathPiece ssh), ("csh", toPathPiece csh)])
|
, menuItemRoute = SomeRoute (CourseNewR, [("tid", toPathPiece tid), ("ssh", toPathPiece ssh), ("csh", toPathPiece csh)])
|
||||||
, menuItemModal = False
|
, menuItemModal = False
|
||||||
, menuItemAccessCallback' = return True
|
, menuItemAccessCallback' = return True
|
||||||
@ -1278,7 +1305,7 @@ pageActions (CourseR tid ssh csh CShowR) =
|
|||||||
, MenuItem
|
, MenuItem
|
||||||
{ menuItemType = PageActionSecondary
|
{ menuItemType = PageActionSecondary
|
||||||
, menuItemLabel = MsgMenuCourseDelete
|
, menuItemLabel = MsgMenuCourseDelete
|
||||||
, menuItemIcon = Nothing
|
, menuItemIcon = Just "trash"
|
||||||
, menuItemRoute = SomeRoute $ CourseR tid ssh csh CDeleteR
|
, menuItemRoute = SomeRoute $ CourseR tid ssh csh CDeleteR
|
||||||
, menuItemModal = False
|
, menuItemModal = False
|
||||||
, menuItemAccessCallback' = return True
|
, menuItemAccessCallback' = return True
|
||||||
@ -1291,29 +1318,19 @@ pageActions (CourseR tid ssh csh SheetListR) =
|
|||||||
, menuItemIcon = Nothing
|
, menuItemIcon = Nothing
|
||||||
, menuItemRoute = SomeRoute $ CourseR tid ssh csh SheetCurrentR
|
, menuItemRoute = SomeRoute $ CourseR tid ssh csh SheetCurrentR
|
||||||
, menuItemModal = False
|
, menuItemModal = False
|
||||||
, menuItemAccessCallback' = do
|
, menuItemAccessCallback' = runDB . maybeT (return False) $ do
|
||||||
now <- liftIO getCurrentTime
|
void . MaybeT $ sheetCurrent tid ssh csh
|
||||||
sheets <- runDB . E.select . E.from $ \(course `E.InnerJoin` sheet) -> do
|
return True
|
||||||
E.on $ sheet E.^. SheetCourse E.==. course E.^. CourseId
|
|
||||||
E.where_ $ sheet E.^. SheetActiveTo E.>. E.val now
|
|
||||||
E.&&. sheet E.^. SheetActiveFrom E.<=. E.val now
|
|
||||||
E.&&. course E.^. CourseTerm E.==. E.val tid
|
|
||||||
E.&&. course E.^. CourseSchool E.==. E.val ssh
|
|
||||||
E.&&. course E.^. CourseShorthand E.==. E.val csh
|
|
||||||
E.orderBy [E.asc $ sheet E.^. SheetActiveTo]
|
|
||||||
E.limit 1
|
|
||||||
return $ sheet E.^. SheetName
|
|
||||||
case sheets of
|
|
||||||
(E.Value shn):_ -> (== Authorized) <$> isAuthorized (CSheetR tid ssh csh shn SShowR) False
|
|
||||||
_ -> return False
|
|
||||||
}
|
}
|
||||||
, MenuItem
|
, MenuItem
|
||||||
{ menuItemType = PageActionPrime
|
{ menuItemType = PageActionPrime
|
||||||
, menuItemLabel = MsgMenuSheetLastInactive
|
, menuItemLabel = MsgMenuSheetOldUnassigned
|
||||||
, menuItemIcon = Nothing
|
, menuItemIcon = Nothing
|
||||||
, menuItemRoute = SomeRoute $ CourseR tid ssh csh SheetLastInactiveR
|
, menuItemRoute = SomeRoute $ CourseR tid ssh csh SheetOldUnassigned
|
||||||
, menuItemModal = False
|
, menuItemModal = False
|
||||||
, menuItemAccessCallback' = (== Authorized) <$> evalAccessCorrector tid ssh csh
|
, menuItemAccessCallback' = runDB . maybeT (return False) $ do
|
||||||
|
void . MaybeT $ sheetOldUnassigned tid ssh csh
|
||||||
|
return True
|
||||||
}
|
}
|
||||||
, MenuItem
|
, MenuItem
|
||||||
{ menuItemType = PageActionPrime
|
{ menuItemType = PageActionPrime
|
||||||
@ -1368,18 +1385,6 @@ pageActions (CSheetR tid ssh csh shn SShowR) =
|
|||||||
guard $ null submissions
|
guard $ null submissions
|
||||||
return True
|
return True
|
||||||
}
|
}
|
||||||
, MenuItem
|
|
||||||
{ menuItemType = PageActionPrime
|
|
||||||
, menuItemLabel = MsgMenuCorrectionsOwn
|
|
||||||
, menuItemIcon = Nothing
|
|
||||||
, menuItemRoute = SomeRoute (CorrectionsR, [ ("corrections-term" , termToText $ unTermKey tid)
|
|
||||||
, ("corrections-school", CI.original $ unSchoolKey ssh)
|
|
||||||
, ("corrections-course", CI.original csh)
|
|
||||||
, ("corrections-sheet" , CI.original shn)
|
|
||||||
])
|
|
||||||
, menuItemModal = False
|
|
||||||
, menuItemAccessCallback' = (== Authorized) <$> evalAccessCorrector tid ssh csh
|
|
||||||
}
|
|
||||||
, MenuItem
|
, MenuItem
|
||||||
{ menuItemType = PageActionPrime
|
{ menuItemType = PageActionPrime
|
||||||
, menuItemLabel = MsgMenuSubmissionOwn
|
, menuItemLabel = MsgMenuSubmissionOwn
|
||||||
@ -1392,6 +1397,18 @@ pageActions (CSheetR tid ssh csh shn SShowR) =
|
|||||||
guard . not $ null submissions
|
guard . not $ null submissions
|
||||||
return True
|
return True
|
||||||
}
|
}
|
||||||
|
, MenuItem
|
||||||
|
{ menuItemType = PageActionPrime
|
||||||
|
, menuItemLabel = MsgMenuCorrectionsOwn
|
||||||
|
, menuItemIcon = Nothing
|
||||||
|
, menuItemRoute = SomeRoute (CorrectionsR, [ ("corrections-term" , termToText $ unTermKey tid)
|
||||||
|
, ("corrections-school", CI.original $ unSchoolKey ssh)
|
||||||
|
, ("corrections-course", CI.original csh)
|
||||||
|
, ("corrections-sheet" , CI.original shn)
|
||||||
|
])
|
||||||
|
, menuItemModal = False
|
||||||
|
, menuItemAccessCallback' = (== Authorized) <$> evalAccessCorrector tid ssh csh
|
||||||
|
}
|
||||||
, MenuItem
|
, MenuItem
|
||||||
{ menuItemType = PageActionPrime
|
{ menuItemType = PageActionPrime
|
||||||
, menuItemLabel = MsgMenuCorrectors
|
, menuItemLabel = MsgMenuCorrectors
|
||||||
@ -1416,10 +1433,18 @@ pageActions (CSheetR tid ssh csh shn SShowR) =
|
|||||||
, menuItemModal = False
|
, menuItemModal = False
|
||||||
, menuItemAccessCallback' = return True
|
, menuItemAccessCallback' = return True
|
||||||
}
|
}
|
||||||
|
, MenuItem
|
||||||
|
{ menuItemType = PageActionSecondary
|
||||||
|
, menuItemLabel = MsgMenuSheetClone
|
||||||
|
, menuItemIcon = Just "copy"
|
||||||
|
, menuItemRoute = SomeRoute (CourseR tid ssh csh SheetNewR, [("shn", toPathPiece shn)])
|
||||||
|
, menuItemModal = False
|
||||||
|
, menuItemAccessCallback' = return True
|
||||||
|
}
|
||||||
, MenuItem
|
, MenuItem
|
||||||
{ menuItemType = PageActionSecondary
|
{ menuItemType = PageActionSecondary
|
||||||
, menuItemLabel = MsgMenuSheetDelete
|
, menuItemLabel = MsgMenuSheetDelete
|
||||||
, menuItemIcon = Nothing
|
, menuItemIcon = Just "trash"
|
||||||
, menuItemRoute = SomeRoute $ CSheetR tid ssh csh shn SDelR
|
, menuItemRoute = SomeRoute $ CSheetR tid ssh csh shn SDelR
|
||||||
, menuItemModal = False
|
, menuItemModal = False
|
||||||
, menuItemAccessCallback' = return True
|
, menuItemAccessCallback' = return True
|
||||||
@ -1455,7 +1480,7 @@ pageActions (CSubmissionR tid ssh csh shn cid SubShowR) =
|
|||||||
, MenuItem
|
, MenuItem
|
||||||
{ menuItemType = PageActionSecondary
|
{ menuItemType = PageActionSecondary
|
||||||
, menuItemLabel = MsgMenuSubmissionDelete
|
, menuItemLabel = MsgMenuSubmissionDelete
|
||||||
, menuItemIcon = Nothing
|
, menuItemIcon = Just "trash"
|
||||||
, menuItemRoute = SomeRoute $ CSubmissionR tid ssh csh shn cid SubDelR
|
, menuItemRoute = SomeRoute $ CSubmissionR tid ssh csh shn cid SubDelR
|
||||||
, menuItemModal = False
|
, menuItemModal = False
|
||||||
, menuItemAccessCallback' = return True
|
, menuItemAccessCallback' = return True
|
||||||
@ -1474,7 +1499,7 @@ pageActions (CSubmissionR tid ssh csh shn cid CorrectionR) =
|
|||||||
pageActions (CSheetR tid ssh csh shn SCorrR) =
|
pageActions (CSheetR tid ssh csh shn SCorrR) =
|
||||||
[ MenuItem
|
[ MenuItem
|
||||||
{ menuItemType = PageActionPrime
|
{ menuItemType = PageActionPrime
|
||||||
, menuItemLabel = MsgMenuCorrections
|
, menuItemLabel = MsgMenuSubmissions
|
||||||
, menuItemIcon = Nothing
|
, menuItemIcon = Nothing
|
||||||
, menuItemRoute = SomeRoute $ CSheetR tid ssh csh shn SSubsR
|
, menuItemRoute = SomeRoute $ CSheetR tid ssh csh shn SSubsR
|
||||||
, menuItemModal = False
|
, menuItemModal = False
|
||||||
|
|||||||
@ -13,8 +13,6 @@ import Control.Monad.Trans.Except
|
|||||||
-- import Data.Function ((&))
|
-- import Data.Function ((&))
|
||||||
-- import Yesod.Form.Bootstrap3
|
-- import Yesod.Form.Bootstrap3
|
||||||
|
|
||||||
import Web.PathPieces (showToPathPiece, readFromPathPiece)
|
|
||||||
|
|
||||||
import Database.Persist.Sql (fromSqlKey)
|
import Database.Persist.Sql (fromSqlKey)
|
||||||
|
|
||||||
-- import Colonnade hiding (fromMaybe)
|
-- import Colonnade hiding (fromMaybe)
|
||||||
@ -23,19 +21,19 @@ import Database.Persist.Sql (fromSqlKey)
|
|||||||
-- import qualified Data.UUID.Cryptographic as UUID
|
-- import qualified Data.UUID.Cryptographic as UUID
|
||||||
|
|
||||||
-- BEGIN - Buttons needed only here
|
-- BEGIN - Buttons needed only here
|
||||||
data CreateButton = CreateMath | CreateInf -- Dummy for Example
|
data ButtonCreate = CreateMath | CreateInf -- Dummy for Example
|
||||||
deriving (Enum, Eq, Ord, Bounded, Read, Show)
|
deriving (Enum, Eq, Ord, Bounded, Read, Show, Generic, Typeable)
|
||||||
|
instance Universe ButtonCreate
|
||||||
|
instance Finite ButtonCreate
|
||||||
|
|
||||||
instance PathPiece CreateButton where -- for displaying the button only, not really for paths
|
nullaryPathPiece ''ButtonCreate camelToPathPiece
|
||||||
toPathPiece = showToPathPiece
|
|
||||||
fromPathPiece = readFromPathPiece
|
|
||||||
|
|
||||||
instance Button UniWorX CreateButton where
|
instance Button UniWorX ButtonCreate where
|
||||||
label CreateMath = [whamlet|Ma<i>thema</i>tik|]
|
btnLabel CreateMath = [whamlet|Ma<i>thema</i>tik|]
|
||||||
label CreateInf = "Informatik"
|
btnLabel CreateInf = "Informatik"
|
||||||
|
|
||||||
cssClass CreateMath = BCInfo
|
btnClasses CreateMath = [BCIsButton, BCInfo]
|
||||||
cssClass CreateInf = BCPrimary
|
btnClasses CreateInf = [BCIsButton, BCPrimary]
|
||||||
-- END Button needed here
|
-- END Button needed here
|
||||||
|
|
||||||
emailTestForm :: AForm (HandlerT UniWorX IO) (Email, MailContext)
|
emailTestForm :: AForm (HandlerT UniWorX IO) (Email, MailContext)
|
||||||
@ -60,7 +58,7 @@ emailTestForm = (,)
|
|||||||
getAdminTestR, postAdminTestR :: Handler Html -- Demo Page. Referenzimplementierungen sollte hier gezeigt werden!
|
getAdminTestR, postAdminTestR :: Handler Html -- Demo Page. Referenzimplementierungen sollte hier gezeigt werden!
|
||||||
getAdminTestR = postAdminTestR
|
getAdminTestR = postAdminTestR
|
||||||
postAdminTestR = do
|
postAdminTestR = do
|
||||||
((btnResult, btnWdgt), btnEnctype) <- runFormPost $ identifyForm "buttons" (buttonForm :: Form CreateButton)
|
((btnResult, btnWdgt), btnEnctype) <- runFormPost $ identifyForm "buttons" (buttonForm :: Form ButtonCreate)
|
||||||
case btnResult of
|
case btnResult of
|
||||||
(FormSuccess CreateInf) -> addMessage Info "Informatik-Knopf gedrückt"
|
(FormSuccess CreateInf) -> addMessage Info "Informatik-Knopf gedrückt"
|
||||||
(FormSuccess CreateMath) -> addMessage Warning "Knopf Mathematik erkannt"
|
(FormSuccess CreateMath) -> addMessage Warning "Knopf Mathematik erkannt"
|
||||||
|
|||||||
@ -479,11 +479,9 @@ assignAction selId = ( CorrSetCorrector
|
|||||||
|
|
||||||
E.distinct $ return user
|
E.distinct $ return user
|
||||||
|
|
||||||
mr <- getMessageRender
|
correctors' <- forM correctors $ \Entity{ entityKey, entityVal = User{..} } -> (SomeMessage userDisplayName, ) <$> encrypt entityKey
|
||||||
|
|
||||||
correctors' <- fmap ((mr MsgNoCorrector, Nothing) :) . forM correctors $ \Entity{ entityKey, entityVal = User{..} } -> (display userDisplayName, ) . Just <$> encrypt entityKey
|
cId <- wopt (selectFieldList correctors' :: Field (HandlerT UniWorX IO) CryptoUUIDUser) (fslI MsgCorrector) Nothing
|
||||||
|
|
||||||
cId <- wpreq (selectFieldList correctors' :: Field (HandlerT UniWorX IO) (Maybe CryptoUUIDUser)) (fslI MsgCorrector) Nothing
|
|
||||||
fmap CorrSetCorrectorData <$> (traverse.traverse) decrypt cId
|
fmap CorrSetCorrectorData <$> (traverse.traverse) decrypt cId
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -259,25 +259,31 @@ getTermCourseListR tid = do
|
|||||||
getCShowR :: TermId -> SchoolId -> CourseShorthand -> Handler Html
|
getCShowR :: TermId -> SchoolId -> CourseShorthand -> Handler Html
|
||||||
getCShowR tid ssh csh = do
|
getCShowR tid ssh csh = do
|
||||||
mbAid <- maybeAuthId
|
mbAid <- maybeAuthId
|
||||||
(courseEnt,(schoolMB,participants,registered),lecturers) <- runDB $ do
|
(course,schoolName,participants,registered,lecturers) <- runDB . maybeT notFound $ do
|
||||||
courseEnt@(Entity cid course) <- getBy404 $ TermSchoolCourseShort tid ssh csh
|
[(E.Entity cid course, E.Value schoolName, E.Value participants, E.Value registered)]
|
||||||
dependent <- (,,)
|
<- lift . E.select . E.from $
|
||||||
<$> get (courseSchool course) -- join -- just fetch full school name here
|
\((school `E.InnerJoin` course) `E.LeftOuterJoin` participant) -> do
|
||||||
<*> count [CourseParticipantCourse ==. cid] -- join
|
E.on $ E.just (course E.^. CourseId) E.==. participant E.?. CourseParticipantCourse
|
||||||
<*> (case mbAid of -- TODO: Someone please refactor this late-night mess here!
|
E.&&. E.val mbAid E.==. participant E.?. CourseParticipantUser
|
||||||
Nothing -> return False
|
E.on $ course E.^. CourseSchool E.==. school E.^. SchoolId
|
||||||
(Just aid) -> do regL <- getBy (UniqueParticipant aid cid)
|
E.where_ $ course E.^. CourseTerm E.==. E.val tid
|
||||||
return $ isJust regL)
|
E.&&. course E.^. CourseSchool E.==. E.val ssh
|
||||||
lecturers <- E.select $ E.from $ \(lecturer `E.InnerJoin` user) -> do
|
E.&&. course E.^. CourseShorthand E.==. E.val csh
|
||||||
E.on $ lecturer E.^. LecturerUser E.==. user E.^. UserId
|
let numParticipants = E.sub_select . E.from $ \part -> do
|
||||||
E.where_ $ lecturer E.^. LecturerCourse E.==. E.val cid
|
E.where_ $ part E.^. CourseParticipantCourse E.==. course E.^. CourseId
|
||||||
return $ user E.^. UserDisplayName
|
return ( E.countRows :: E.SqlExpr (E.Value Int64))
|
||||||
return (courseEnt,dependent,E.unValue <$> lecturers)
|
return (course,school E.^. SchoolName, numParticipants, participant E.?. CourseParticipantRegistration)
|
||||||
let course = entityVal courseEnt
|
lecturers <- lift . E.select $ E.from $ \(lecturer `E.InnerJoin` user) -> do
|
||||||
(regWidget, regEnctype) <- generateFormPost $ identifyForm "registerBtn" $ registerForm registered $ courseRegisterSecret course
|
E.on $ lecturer E.^. LecturerUser E.==. user E.^. UserId
|
||||||
|
E.where_ $ lecturer E.^. LecturerCourse E.==. E.val cid
|
||||||
|
return $ user E.^. UserDisplayName
|
||||||
|
return (course,schoolName,participants,registered,map E.unValue lecturers)
|
||||||
|
mRegFrom <- traverse (formatTime SelFormatDateTime) $ courseRegisterFrom course
|
||||||
|
mRegTo <- traverse (formatTime SelFormatDateTime) $ courseRegisterTo course
|
||||||
|
mDereg <- traverse (formatTime SelFormatDateTime) $ courseDeregisterUntil course
|
||||||
|
mRegAt <- traverse (formatTime SelFormatDateTime) registered
|
||||||
|
(regWidget, regEnctype) <- generateFormPost $ identifyForm "registerBtn" $ registerForm (isJust mRegAt) $ courseRegisterSecret course
|
||||||
registrationOpen <- (==Authorized) <$> isAuthorized (CourseR tid ssh csh CRegisterR) True
|
registrationOpen <- (==Authorized) <$> isAuthorized (CourseR tid ssh csh CRegisterR) True
|
||||||
mRegFrom <- traverse (formatTime SelFormatDateTime) $ courseRegisterFrom course
|
|
||||||
mRegTo <- traverse (formatTime SelFormatDateTime) $ courseRegisterTo course
|
|
||||||
defaultLayout $ do
|
defaultLayout $ do
|
||||||
setTitle [shamlet| #{toPathPiece tid} - #{csh}|]
|
setTitle [shamlet| #{toPathPiece tid} - #{csh}|]
|
||||||
$(widgetFile "course")
|
$(widgetFile "course")
|
||||||
|
|||||||
@ -222,7 +222,7 @@ getProfileDataR = do
|
|||||||
let tutorialTable = [whamlet|Übungsgruppen werden momentan leider noch nicht unterstützt.|]
|
let tutorialTable = [whamlet|Übungsgruppen werden momentan leider noch nicht unterstützt.|]
|
||||||
|
|
||||||
-- Delete Button
|
-- Delete Button
|
||||||
(btnWdgt, btnEnctype) <- generateFormPost (buttonForm :: Form BtnDelete)
|
(btnWdgt, btnEnctype) <- generateFormPost (buttonForm :: Form ButtonDelete)
|
||||||
defaultLayout $ do
|
defaultLayout $ do
|
||||||
let delWdgt = $(widgetFile "widgets/data-delete")
|
let delWdgt = $(widgetFile "widgets/data-delete")
|
||||||
$(widgetFile "profileData")
|
$(widgetFile "profileData")
|
||||||
|
|||||||
@ -3,6 +3,7 @@ module Handler.Sheet where
|
|||||||
import Import
|
import Import
|
||||||
import System.FilePath (takeFileName)
|
import System.FilePath (takeFileName)
|
||||||
|
|
||||||
|
import Utils.Sheet
|
||||||
import Handler.Utils
|
import Handler.Utils
|
||||||
-- import Handler.Utils.Zip
|
-- import Handler.Utils.Zip
|
||||||
import Handler.Utils.Table.Cells
|
import Handler.Utils.Table.Cells
|
||||||
@ -142,38 +143,15 @@ makeSheetForm msId template = identForm FIDsheet $ \html -> do
|
|||||||
|
|
||||||
getSheetCurrentR :: TermId -> SchoolId -> CourseShorthand -> Handler Html
|
getSheetCurrentR :: TermId -> SchoolId -> CourseShorthand -> Handler Html
|
||||||
getSheetCurrentR tid ssh csh = runDB $ do
|
getSheetCurrentR tid ssh csh = runDB $ do
|
||||||
now <- liftIO getCurrentTime
|
let redi shn = redirectAccess $ CSheetR tid ssh csh shn SShowR
|
||||||
sheets <- E.select . E.from $ \(course `E.InnerJoin` sheet) -> do
|
shn <- sheetCurrent tid ssh csh
|
||||||
E.on $ sheet E.^. SheetCourse E.==. course E.^. CourseId
|
maybe notFound redi shn
|
||||||
E.where_ $ sheet E.^. SheetActiveTo E.>. E.val now
|
|
||||||
E.&&. sheet E.^. SheetActiveFrom E.<=. E.val now
|
|
||||||
E.&&. course E.^. CourseTerm E.==. E.val tid
|
|
||||||
E.&&. course E.^. CourseSchool E.==. E.val ssh
|
|
||||||
E.&&. course E.^. CourseShorthand E.==. E.val csh
|
|
||||||
E.orderBy [E.asc $ sheet E.^. SheetActiveFrom]
|
|
||||||
E.limit 1
|
|
||||||
return $ sheet E.^. SheetName
|
|
||||||
case sheets of
|
|
||||||
(E.Value shn):_ -> redirectAccess $ CSheetR tid ssh csh shn SShowR
|
|
||||||
_ -> notFound
|
|
||||||
|
|
||||||
getSheetLastInactiveR :: TermId -> SchoolId -> CourseShorthand -> Handler Html
|
|
||||||
getSheetLastInactiveR tid ssh csh = runDB $ do
|
|
||||||
-- TODO: deliver oldest sheet with unassigned submissions instead!!!
|
|
||||||
now <- liftIO getCurrentTime
|
|
||||||
sheets <- E.select . E.from $ \(course `E.InnerJoin` sheet) -> do
|
|
||||||
E.on $ sheet E.^. SheetCourse E.==. course E.^. CourseId
|
|
||||||
E.where_ $ sheet E.^. SheetActiveTo E.<=. E.val now
|
|
||||||
E.&&. course E.^. CourseTerm E.==. E.val tid
|
|
||||||
E.&&. course E.^. CourseSchool E.==. E.val ssh
|
|
||||||
E.&&. course E.^. CourseShorthand E.==. E.val csh
|
|
||||||
E.orderBy [E.desc $ sheet E.^. SheetActiveTo]
|
|
||||||
E.limit 1
|
|
||||||
return $ sheet E.^. SheetName
|
|
||||||
case sheets of
|
|
||||||
(E.Value shn):_ -> redirectAccess $ CSheetR tid ssh csh shn SShowR
|
|
||||||
_ -> notFound
|
|
||||||
|
|
||||||
|
getSheetOldUnassigned :: TermId -> SchoolId -> CourseShorthand -> Handler ()
|
||||||
|
getSheetOldUnassigned tid ssh csh = runDB $ do
|
||||||
|
let redi shn = redirectAccess $ CSheetR tid ssh csh shn SSubsR
|
||||||
|
shn <- sheetOldUnassigned tid ssh csh
|
||||||
|
maybe notFound redi shn
|
||||||
|
|
||||||
getSheetListR :: TermId -> SchoolId -> CourseShorthand -> Handler Html
|
getSheetListR :: TermId -> SchoolId -> CourseShorthand -> Handler Html
|
||||||
getSheetListR tid ssh csh = do
|
getSheetListR tid ssh csh = do
|
||||||
@ -299,15 +277,15 @@ getSheetListR tid ssh csh = do
|
|||||||
$(widgetFile "sheetList")
|
$(widgetFile "sheetList")
|
||||||
|
|
||||||
data ButtonGeneratePseudonym = BtnGenerate
|
data ButtonGeneratePseudonym = BtnGenerate
|
||||||
deriving (Enum, Eq, Ord, Bounded, Read, Show)
|
deriving (Enum, Eq, Ord, Bounded, Read, Show, Generic, Typeable)
|
||||||
instance Universe ButtonGeneratePseudonym
|
instance Universe ButtonGeneratePseudonym
|
||||||
instance Finite ButtonGeneratePseudonym
|
instance Finite ButtonGeneratePseudonym
|
||||||
|
|
||||||
nullaryPathPiece ''ButtonGeneratePseudonym (camelToPathPiece' 1)
|
nullaryPathPiece ''ButtonGeneratePseudonym (camelToPathPiece' 1)
|
||||||
|
|
||||||
instance Button UniWorX ButtonGeneratePseudonym where
|
instance Button UniWorX ButtonGeneratePseudonym where
|
||||||
label BtnGenerate = [whamlet|_{MsgSheetGeneratePseudonym}|]
|
btnLabel BtnGenerate = [whamlet|_{MsgSheetGeneratePseudonym}|]
|
||||||
cssClass BtnGenerate = BCDefault
|
btnClasses BtnGenerate = [BCIsButton, BCDefault]
|
||||||
|
|
||||||
-- Show single sheet
|
-- Show single sheet
|
||||||
getSShowR :: TermId -> SchoolId -> CourseShorthand -> SheetName -> Handler Html
|
getSShowR :: TermId -> SchoolId -> CourseShorthand -> SheetName -> Handler Html
|
||||||
@ -447,11 +425,17 @@ getSFileR tid ssh csh shn typ title = do
|
|||||||
|
|
||||||
getSheetNewR :: TermId -> SchoolId -> CourseShorthand -> Handler Html
|
getSheetNewR :: TermId -> SchoolId -> CourseShorthand -> Handler Html
|
||||||
getSheetNewR tid ssh csh = do
|
getSheetNewR tid ssh csh = do
|
||||||
|
parShn <- runInputGetResult $ iopt ciField "shn"
|
||||||
|
let searchShn sheet = case parShn of
|
||||||
|
(FormSuccess (Just shn)) -> E.where_ $ sheet E.^. SheetName E.==. E.val shn
|
||||||
|
-- (FormFailure msgs) -> -- not in MonadHandler anymore -- forM_ msgs (addMessage Error . toHtml)
|
||||||
|
_other -> return ()
|
||||||
lastSheets <- runDB $ E.select . E.from $ \(course `E.InnerJoin` sheet) -> do
|
lastSheets <- runDB $ E.select . E.from $ \(course `E.InnerJoin` sheet) -> do
|
||||||
E.on $ course E.^. CourseId E.==. sheet E.^. SheetCourse
|
E.on $ course E.^. CourseId E.==. sheet E.^. SheetCourse
|
||||||
E.where_ $ course E.^. CourseTerm E.==. E.val tid
|
E.where_ $ course E.^. CourseTerm E.==. E.val tid
|
||||||
E.&&. course E.^. CourseSchool E.==. E.val ssh
|
E.&&. course E.^. CourseSchool E.==. E.val ssh
|
||||||
E.&&. course E.^. CourseShorthand E.==. E.val csh
|
E.&&. course E.^. CourseShorthand E.==. E.val csh
|
||||||
|
searchShn sheet
|
||||||
-- let lastSheetEdit = E.sub_select . E.from $ \sheetEdit -> do
|
-- let lastSheetEdit = E.sub_select . E.from $ \sheetEdit -> do
|
||||||
-- E.where_ $ sheetEdit E.^. SheetEditSheet E.==. sheet E.^. SheetId
|
-- E.where_ $ sheetEdit E.^. SheetEditSheet E.==. sheet E.^. SheetId
|
||||||
-- return . E.max_ $ sheetEdit E.^. SheetEditTime
|
-- return . E.max_ $ sheetEdit E.^. SheetEditTime
|
||||||
|
|||||||
@ -69,3 +69,8 @@ warnTermDays tid times = do
|
|||||||
forM_ warnholidays $ warnI MsgDayIsAHoliday
|
forM_ warnholidays $ warnI MsgDayIsAHoliday
|
||||||
forM_ outoflecture $ warnI MsgDayIsOutOfLecture
|
forM_ outoflecture $ warnI MsgDayIsOutOfLecture
|
||||||
forM_ outoftermdays $ warnI MsgDayIsOutOfTerm
|
forM_ outoftermdays $ warnI MsgDayIsOutOfTerm
|
||||||
|
|
||||||
|
visibleWidget :: Bool -> Widget
|
||||||
|
-- ^ @visibleWidget False@ is an icon that denotes that something™ is not visible
|
||||||
|
visibleWidget True = mempty
|
||||||
|
visibleWidget False = [whamlet|<i .fas .fa-eye-slash>|]
|
||||||
|
|||||||
@ -15,8 +15,6 @@ import qualified Data.Char as Char
|
|||||||
|
|
||||||
import qualified Data.CaseInsensitive as CI
|
import qualified Data.CaseInsensitive as CI
|
||||||
|
|
||||||
import qualified Data.Foldable as Foldable
|
|
||||||
|
|
||||||
-- import Yesod.Core
|
-- import Yesod.Core
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
-- import Yesod.Form.Types
|
-- import Yesod.Form.Types
|
||||||
@ -51,64 +49,55 @@ import Data.Aeson.Text (encodeToLazyText)
|
|||||||
-- Buttons (new version ) --
|
-- Buttons (new version ) --
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
data BtnDelete = BtnDelete
|
data ButtonDelete = BtnDelete
|
||||||
deriving (Enum, Eq, Ord, Bounded, Read, Show)
|
deriving (Enum, Eq, Ord, Bounded, Read, Show, Generic, Typeable)
|
||||||
|
instance Universe ButtonDelete
|
||||||
|
instance Finite ButtonDelete
|
||||||
|
|
||||||
instance Universe BtnDelete
|
nullaryPathPiece ''ButtonDelete $ camelToPathPiece' 1
|
||||||
instance Finite BtnDelete
|
|
||||||
|
|
||||||
nullaryPathPiece ''BtnDelete $ camelToPathPiece' 1
|
embedRenderMessage ''UniWorX ''ButtonDelete id
|
||||||
|
instance Button UniWorX ButtonDelete where
|
||||||
|
btnClasses BtnDelete = [BCIsButton, BCDanger]
|
||||||
|
|
||||||
instance Button UniWorX BtnDelete where
|
data ButtonRegister = BtnRegister | BtnDeregister
|
||||||
label BtnDelete = [whamlet|_{MsgBtnDelete}|]
|
deriving (Enum, Eq, Ord, Bounded, Read, Show, Generic, Typeable)
|
||||||
|
instance Universe ButtonRegister
|
||||||
|
instance Finite ButtonRegister
|
||||||
|
|
||||||
cssClass BtnDelete = BCDanger
|
nullaryPathPiece ''ButtonRegister $ camelToPathPiece' 1
|
||||||
|
|
||||||
data RegisterButton = BtnRegister | BtnDeregister
|
embedRenderMessage ''UniWorX ''ButtonRegister id
|
||||||
deriving (Enum, Eq, Ord, Bounded, Read, Show)
|
instance Button UniWorX ButtonRegister where
|
||||||
|
btnClasses BtnRegister = [BCIsButton, BCPrimary]
|
||||||
|
btnClasses BtnDeregister = [BCIsButton, BCDanger]
|
||||||
|
|
||||||
instance Universe RegisterButton
|
data ButtonHijack = BtnHijack
|
||||||
instance Finite RegisterButton
|
deriving (Enum, Eq, Ord, Bounded, Read, Show, Generic, Typeable)
|
||||||
|
instance Universe ButtonHijack
|
||||||
|
instance Finite ButtonHijack
|
||||||
|
|
||||||
nullaryPathPiece ''RegisterButton $ camelToPathPiece' 1
|
nullaryPathPiece ''ButtonHijack $ camelToPathPiece' 1
|
||||||
|
|
||||||
instance Button UniWorX RegisterButton where
|
embedRenderMessage ''UniWorX ''ButtonHijack id
|
||||||
label BtnRegister = [whamlet|_{MsgBtnRegister}|]
|
instance Button UniWorX ButtonHijack where
|
||||||
label BtnDeregister = [whamlet|_{MsgBtnDeregister}|]
|
btnClasses BtnHijack = [BCIsButton, BCDefault]
|
||||||
|
|
||||||
cssClass BtnRegister = BCPrimary
|
data ButtonSubmitDelete = BtnSubmit' | BtnDelete'
|
||||||
cssClass BtnDeregister = BCDanger
|
deriving (Enum, Eq, Ord, Bounded, Read, Show, Generic, Typeable)
|
||||||
|
|
||||||
data AdminHijackUserButton = BtnHijack
|
instance Universe ButtonSubmitDelete
|
||||||
deriving (Enum, Eq, Ord, Bounded, Read, Show)
|
instance Finite ButtonSubmitDelete
|
||||||
|
|
||||||
instance Universe AdminHijackUserButton
|
embedRenderMessage ''UniWorX ''ButtonSubmitDelete $ dropSuffix "'"
|
||||||
instance Finite AdminHijackUserButton
|
instance Button UniWorX ButtonSubmitDelete where
|
||||||
|
btnClasses BtnSubmit' = [BCIsButton, BCPrimary]
|
||||||
nullaryPathPiece ''AdminHijackUserButton $ camelToPathPiece' 1
|
btnClasses BtnDelete' = [BCIsButton, BCDanger]
|
||||||
|
|
||||||
instance Button UniWorX AdminHijackUserButton where
|
|
||||||
label BtnHijack = [whamlet|_{MsgBtnHijack}|]
|
|
||||||
|
|
||||||
cssClass BtnHijack = BCDefault
|
|
||||||
|
|
||||||
data BtnSubmitDelete = BtnSubmit' | BtnDelete'
|
|
||||||
deriving (Enum, Eq, Ord, Bounded, Read, Show)
|
|
||||||
|
|
||||||
instance Universe BtnSubmitDelete
|
|
||||||
instance Finite BtnSubmitDelete
|
|
||||||
|
|
||||||
instance Button UniWorX BtnSubmitDelete where
|
|
||||||
label BtnSubmit' = [whamlet|_{MsgBtnSubmit}|]
|
|
||||||
label BtnDelete' = [whamlet|_{MsgBtnDelete}|]
|
|
||||||
|
|
||||||
cssClass BtnSubmit' = BCPrimary
|
|
||||||
cssClass BtnDelete' = BCDanger
|
|
||||||
|
|
||||||
btnValidate _ BtnSubmit' = True
|
btnValidate _ BtnSubmit' = True
|
||||||
btnValidate _ BtnDelete' = False
|
btnValidate _ BtnDelete' = False
|
||||||
|
|
||||||
nullaryPathPiece ''BtnSubmitDelete $ camelToPathPiece' 1 . dropSuffix "'"
|
nullaryPathPiece ''ButtonSubmitDelete $ camelToPathPiece' 1 . dropSuffix "'"
|
||||||
|
|
||||||
|
|
||||||
-- -- Looks like a button, but is just a link (e.g. for create course, etc.)
|
-- -- Looks like a button, but is just a link (e.g. for create course, etc.)
|
||||||
@ -118,8 +107,14 @@ nullaryPathPiece ''BtnSubmitDelete $ camelToPathPiece' 1 . dropSuffix "'"
|
|||||||
-- instance PathPiece LinkButton where
|
-- instance PathPiece LinkButton where
|
||||||
-- LinkButton route = ???
|
-- LinkButton route = ???
|
||||||
|
|
||||||
linkButton :: Widget -> ButtonCssClass UniWorX -> Route UniWorX -> Widget -- Alternative: Handler.Utils.simpleLink
|
linkButton :: Widget -> [ButtonClass UniWorX] -> SomeRoute UniWorX -> Widget -- Alternative: Handler.Utils.simpleLink
|
||||||
linkButton lbl cls url = [whamlet| <a href=@{url} .btn .#{bcc2txt cls} role=button>^{lbl} |]
|
linkButton lbl cls url = do
|
||||||
|
url' <- toTextUrl url
|
||||||
|
[whamlet|
|
||||||
|
$newline never
|
||||||
|
<a href=#{url'} class=#{unwords $ map toPathPiece cls} role=button>
|
||||||
|
^{lbl}
|
||||||
|
|]
|
||||||
-- [whamlet|
|
-- [whamlet|
|
||||||
-- <form method=post action=@{url}>
|
-- <form method=post action=@{url}>
|
||||||
-- <input type="hidden" name="_formid" value="identify-linkButton">
|
-- <input type="hidden" name="_formid" value="identify-linkButton">
|
||||||
@ -128,31 +123,16 @@ linkButton lbl cls url = [whamlet| <a href=@{url} .btn .#{bcc2txt cls} role=butt
|
|||||||
-- <input .btn .#{bcc2txt cls} type="submit" value=^{lbl}>
|
-- <input .btn .#{bcc2txt cls} type="submit" value=^{lbl}>
|
||||||
|
|
||||||
|
|
||||||
-- buttonForm :: Button a => Markup -> MForm (HandlerT UniWorX IO) (FormResult a, (WidgetT UniWorX IO ()))
|
-- buttonForm :: (Button UniWorX a, Finite a) => Markup -> MForm (HandlerT UniWorX IO) (FormResult a, Widget)
|
||||||
buttonForm :: (Button UniWorX a, Show a) => Form a
|
buttonForm :: (Button UniWorX a, Finite a) => Form a
|
||||||
buttonForm csrf = do
|
buttonForm csrf = do
|
||||||
buttonIdent <- newFormIdent
|
(res, ($ []) -> fViews) <- aFormToForm . disambiguateButtons $ combinedButtonFieldF ""
|
||||||
let button b = mopt (buttonField b) ("n/a"{ fsName = Just buttonIdent }) Nothing
|
return (res, [whamlet|
|
||||||
(results, btnViews) <- unzip <$> mapM button [minBound..maxBound]
|
$newline never
|
||||||
let widget =
|
#{csrf}
|
||||||
[whamlet|
|
$forall bView <- fViews
|
||||||
#{csrf}
|
^{fvInput bView}
|
||||||
$forall bView <- btnViews
|
|])
|
||||||
^{fvInput bView}
|
|
||||||
|]
|
|
||||||
return (accResult results,widget)
|
|
||||||
where
|
|
||||||
accResult :: Foldable f => f (FormResult (Maybe a)) -> FormResult a
|
|
||||||
accResult = Foldable.foldr accResult' FormMissing
|
|
||||||
|
|
||||||
accResult' :: FormResult (Maybe a) -> FormResult a -> FormResult a
|
|
||||||
-- Find the single FormSuccess Just _; Expected behaviour: all buttons deliver FormFailure, except for one.
|
|
||||||
accResult' (FormSuccess (Just _)) (FormSuccess _) = FormFailure ["Ambiguous button parse"]
|
|
||||||
accResult' (FormSuccess (Just x)) _ = FormSuccess x
|
|
||||||
accResult' _ x@(FormSuccess _) = x --Safe: most buttons deliver FormFailure, one delivers FormSuccess
|
|
||||||
accResult' (FormSuccess Nothing) x = x
|
|
||||||
accResult' FormMissing _ = FormMissing
|
|
||||||
accResult' (FormFailure errs) _ = FormFailure errs
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -130,7 +130,9 @@ assignSubmissions sid restriction = do
|
|||||||
props = getSum $ foldMap (Sum . fst) assignments
|
props = getSum $ foldMap (Sum . fst) assignments
|
||||||
|
|
||||||
toDeficit' (prop, assigned) = let
|
toDeficit' (prop, assigned) = let
|
||||||
target = round $ fromInteger assigned' * (prop / props)
|
target
|
||||||
|
| props == 0 = 0
|
||||||
|
| otherwise = round $ fromInteger assigned' * (prop / props)
|
||||||
in target - assigned
|
in target - assigned
|
||||||
|
|
||||||
$logDebugS "assignSubmissions" $ "Previous submissions: " <> tshow prevSubs'
|
$logDebugS "assignSubmissions" $ "Previous submissions: " <> tshow prevSubs'
|
||||||
|
|||||||
@ -519,7 +519,7 @@ dbParamsFormWrap DBParamsForm{..} tableForm frag = do
|
|||||||
return . (res,) $ do
|
return . (res,) $ do
|
||||||
btnId <- newIdent
|
btnId <- newIdent
|
||||||
act <- traverse toTextUrl dbParamsFormAction
|
act <- traverse toTextUrl dbParamsFormAction
|
||||||
let submitField :: Field Handler SubmitButton
|
let submitField :: Field Handler ButtonSubmit
|
||||||
submitField = buttonField BtnSubmit
|
submitField = buttonField BtnSubmit
|
||||||
submitView :: Widget
|
submitView :: Widget
|
||||||
submitView = fieldView submitField btnId "" mempty (Right BtnSubmit) False
|
submitView = fieldView submitField btnId "" mempty (Right BtnSubmit) False
|
||||||
@ -602,11 +602,11 @@ dbTable PSValidator{..} dbtable@DBTable{ dbtIdent = dbtIdent'@(toPathPiece -> db
|
|||||||
| otherwise
|
| otherwise
|
||||||
= def
|
= def
|
||||||
|
|
||||||
|
referencePagesize = psLimit . snd . runPSValidator dbtable $ Just prevPi
|
||||||
|
|
||||||
(((filterRes, filterWdgt), filterEnc), ((pagesizeRes, pagesizeWdgt), pagesizeEnc)) <- mdo
|
(((filterRes, filterWdgt), filterEnc), ((pagesizeRes, pagesizeWdgt), pagesizeEnc)) <- mdo
|
||||||
(filterRes'@((filterRes, _), _)) <- runFormGet . identForm FIDDBTableFilter . addPIHiddenField dbtable (prevPi & _piFilter .~ Nothing & _piPage .~ Nothing & _piLimit .~ (formResult' pagesizeRes <|> piLimit prevPi)) . renderAForm FormDBTableFilter $ dbtFilterUI (piFilter prevPi)
|
(filterRes'@((filterRes, _), _)) <- runFormGet . identForm FIDDBTableFilter . addPIHiddenField dbtable (prevPi & _piFilter .~ Nothing & _piPage .~ Nothing & _piLimit .~ (formResult' pagesizeRes <|> piLimit prevPi)) . renderAForm FormDBTableFilter $ dbtFilterUI (piFilter prevPi)
|
||||||
|
|
||||||
let referencePagesize = psLimit . snd . runPSValidator dbtable $ Just prevPi
|
|
||||||
|
|
||||||
(pagesizeRes'@((pagesizeRes, _), _)) <- lift . runFormGet . identForm FIDDBTablePagesize . addPIHiddenField dbtable (prevPi & _piPage .~ Nothing & _piLimit .~ Nothing & _piFilter .~ (formResult' filterRes <|> piFilter prevPi)) . renderAForm FormDBTablePagesize $
|
(pagesizeRes'@((pagesizeRes, _), _)) <- lift . runFormGet . identForm FIDDBTablePagesize . addPIHiddenField dbtable (prevPi & _piPage .~ Nothing & _piLimit .~ Nothing & _piFilter .~ (formResult' filterRes <|> piFilter prevPi)) . renderAForm FormDBTablePagesize $
|
||||||
areq (pagesizeField referencePagesize) (fslI MsgDBTablePagesize & addAutosubmit & addName (wIdent "pagesize") & addClass "select--pagesize") (Just referencePagesize)
|
areq (pagesizeField referencePagesize) (fslI MsgDBTablePagesize & addAutosubmit & addName (wIdent "pagesize") & addClass "select--pagesize") (Just referencePagesize)
|
||||||
<* autosubmitButton
|
<* autosubmitButton
|
||||||
@ -760,6 +760,15 @@ dbColonnade :: (Headedness h, Monoid x)
|
|||||||
-> Colonnade h r (DBCell (ReaderT SqlBackend (HandlerT UniWorX IO)) x)
|
-> Colonnade h r (DBCell (ReaderT SqlBackend (HandlerT UniWorX IO)) x)
|
||||||
dbColonnade = id
|
dbColonnade = id
|
||||||
|
|
||||||
|
pagesizeOptions :: PagesizeLimit -- ^ Current/previous value
|
||||||
|
-> NonNull [PagesizeLimit]
|
||||||
|
pagesizeOptions psLim = impureNonNull . Set.toAscList . Set.fromList $ psLim : PagesizeAll : map PagesizeLimit opts
|
||||||
|
where
|
||||||
|
opts :: [Int64]
|
||||||
|
opts = filter (> 0) $ opts' <> map (`div` 2) opts'
|
||||||
|
|
||||||
|
opts' = [ 10^n | n <- [1..3]]
|
||||||
|
|
||||||
pagesizeField :: PagesizeLimit -> Field Handler PagesizeLimit
|
pagesizeField :: PagesizeLimit -> Field Handler PagesizeLimit
|
||||||
pagesizeField psLim = selectField $ do
|
pagesizeField psLim = selectField $ do
|
||||||
MsgRenderer mr <- getMsgRenderer
|
MsgRenderer mr <- getMsgRenderer
|
||||||
@ -767,16 +776,9 @@ pagesizeField psLim = selectField $ do
|
|||||||
optText (PagesizeLimit l) = tshow l
|
optText (PagesizeLimit l) = tshow l
|
||||||
optText PagesizeAll = mr MsgDBTablePagesizeAll
|
optText PagesizeAll = mr MsgDBTablePagesizeAll
|
||||||
|
|
||||||
toOptionList = flip OptionList fromPathPiece . map (\o -> Option (optText o) o $ toPathPiece o) . Set.toAscList . Set.fromList
|
toOptionList = flip OptionList fromPathPiece . map (\o -> Option (optText o) o $ toPathPiece o)
|
||||||
return $ toOptionList limOpts
|
return . toOptionList . toNullable $ pagesizeOptions psLim
|
||||||
where
|
|
||||||
limOpts :: [PagesizeLimit]
|
|
||||||
limOpts = psLim : PagesizeAll : map PagesizeLimit opts
|
|
||||||
|
|
||||||
opts :: [Int64]
|
|
||||||
opts = filter (> 0) $ opts' <> map (`div` 2) opts'
|
|
||||||
|
|
||||||
opts' = [ 10^n | n <- [1..3]]
|
|
||||||
|
|
||||||
--- DBCell utility functions
|
--- DBCell utility functions
|
||||||
|
|
||||||
|
|||||||
@ -59,6 +59,7 @@ import Ldap.Client.Pool as Import
|
|||||||
|
|
||||||
import Database.Esqueleto.Instances as Import ()
|
import Database.Esqueleto.Instances as Import ()
|
||||||
import Database.Persist.Sql.Instances as Import ()
|
import Database.Persist.Sql.Instances as Import ()
|
||||||
|
import Database.Persist.Sql as Import (SqlReadT,SqlWriteT)
|
||||||
|
|
||||||
|
|
||||||
import Control.Monad.Trans.RWS (RWST)
|
import Control.Monad.Trans.RWS (RWST)
|
||||||
|
|||||||
@ -364,7 +364,7 @@ mcons :: Maybe a -> [a] -> [a]
|
|||||||
mcons Nothing xs = xs
|
mcons Nothing xs = xs
|
||||||
mcons (Just x) xs = x:xs
|
mcons (Just x) xs = x:xs
|
||||||
|
|
||||||
newtype NTop a = NTop a -- treat Nothing as Top for Ord (Maybe a); default implementation treats Nothing as bottom
|
newtype NTop a = NTop { nBot :: a } -- treat Nothing as Top for Ord (Maybe a); default implementation treats Nothing as bottom
|
||||||
|
|
||||||
instance Eq a => Eq (NTop (Maybe a)) where
|
instance Eq a => Eq (NTop (Maybe a)) where
|
||||||
(NTop x) == (NTop y) = x == y
|
(NTop x) == (NTop y) = x == y
|
||||||
|
|||||||
@ -7,7 +7,6 @@ import Settings
|
|||||||
|
|
||||||
import qualified Text.Blaze.Internal as Blaze (null)
|
import qualified Text.Blaze.Internal as Blaze (null)
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import qualified Data.Char as Char
|
|
||||||
|
|
||||||
import Data.CaseInsensitive (CI)
|
import Data.CaseInsensitive (CI)
|
||||||
import qualified Data.CaseInsensitive as CI
|
import qualified Data.CaseInsensitive as CI
|
||||||
@ -200,37 +199,36 @@ identForm = identifyForm . toPathPiece
|
|||||||
-- Buttons (new version ) --
|
-- Buttons (new version ) --
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
data family ButtonCssClass site :: *
|
data family ButtonClass site :: *
|
||||||
|
|
||||||
bcc2txt :: Show (ButtonCssClass site) => ButtonCssClass site -> Text -- a Hack; maybe define Read/Show manually
|
class (PathPiece a, PathPiece (ButtonClass site), RenderMessage site ButtonMessage) => Button site a where
|
||||||
bcc2txt bcc = T.pack $ "btn-" ++ (Char.toLower <$> drop 2 (show bcc))
|
btnLabel :: a -> WidgetT site IO ()
|
||||||
|
|
||||||
class (Enum a, Bounded a, Ord a, PathPiece a) => Button site a where
|
default btnLabel :: RenderMessage site a => a -> WidgetT site IO ()
|
||||||
label :: a -> WidgetT site IO ()
|
btnLabel = toWidget <=< ap getMessageRender . return
|
||||||
label = toWidget . toPathPiece
|
|
||||||
|
|
||||||
btnValidate :: forall p. p site -> a -> Bool
|
btnValidate :: forall p. p site -> a -> Bool
|
||||||
btnValidate _ _ = True
|
btnValidate _ _ = True
|
||||||
|
|
||||||
cssClass :: a -> ButtonCssClass site
|
btnClasses :: a -> [ButtonClass site]
|
||||||
|
btnClasses _ = []
|
||||||
|
|
||||||
data ButtonMessage = MsgAmbiguousButtons
|
data ButtonMessage = MsgAmbiguousButtons
|
||||||
| MsgWrongButtonValue
|
| MsgWrongButtonValue
|
||||||
| MsgMultipleButtonValues
|
| MsgMultipleButtonValues
|
||||||
|
deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic, Typeable)
|
||||||
|
|
||||||
data SubmitButton = BtnSubmit
|
data ButtonSubmit = BtnSubmit
|
||||||
deriving (Enum, Eq, Ord, Bounded, Read, Show)
|
deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic, Typeable)
|
||||||
|
|
||||||
instance Universe SubmitButton
|
instance Universe ButtonSubmit
|
||||||
instance Finite SubmitButton
|
instance Finite ButtonSubmit
|
||||||
|
|
||||||
nullaryPathPiece ''SubmitButton $ camelToPathPiece' 1
|
nullaryPathPiece ''ButtonSubmit $ camelToPathPiece' 1
|
||||||
|
|
||||||
buttonField :: forall a m.
|
buttonField :: forall a m.
|
||||||
( Button (HandlerSite m) a
|
( Button (HandlerSite m) a
|
||||||
, Show (ButtonCssClass (HandlerSite m))
|
, MonadHandler m
|
||||||
, RenderMessage (HandlerSite m) ButtonMessage
|
|
||||||
, Monad m
|
|
||||||
) => a -> Field m a
|
) => a -> Field m a
|
||||||
-- | Already validates that the correct button press was received (result only neccessary for combinedButtonField)
|
-- | Already validates that the correct button press was received (result only neccessary for combinedButtonField)
|
||||||
buttonField btn = Field{..}
|
buttonField btn = Field{..}
|
||||||
@ -239,12 +237,12 @@ buttonField btn = Field{..}
|
|||||||
|
|
||||||
fieldView :: FieldViewFunc m a
|
fieldView :: FieldViewFunc m a
|
||||||
fieldView fid name attrs _val _ = let
|
fieldView fid name attrs _val _ = let
|
||||||
cssClass' :: ButtonCssClass (HandlerSite m)
|
|
||||||
cssClass' = cssClass btn
|
|
||||||
validate = btnValidate (Proxy @(HandlerSite m)) btn
|
validate = btnValidate (Proxy @(HandlerSite m)) btn
|
||||||
|
classes :: [ButtonClass (HandlerSite m)]
|
||||||
|
classes = btnClasses btn
|
||||||
in [whamlet|
|
in [whamlet|
|
||||||
$newline never
|
$newline never
|
||||||
<button .btn .#{bcc2txt cssClass'} type=submit name=#{name} value=#{toPathPiece btn} *{attrs} ##{fid} :not validate:formnovalidate>^{label btn}
|
<button class=#{unwords $ map toPathPiece classes} type=submit name=#{name} value=#{toPathPiece btn} *{attrs} ##{fid} :not validate:formnovalidate>^{btnLabel btn}
|
||||||
|]
|
|]
|
||||||
|
|
||||||
fieldParse [] [] = return $ Right Nothing
|
fieldParse [] [] = return $ Right Nothing
|
||||||
@ -255,8 +253,6 @@ buttonField btn = Field{..}
|
|||||||
|
|
||||||
combinedButtonField :: forall a m.
|
combinedButtonField :: forall a m.
|
||||||
( Button (HandlerSite m) a
|
( Button (HandlerSite m) a
|
||||||
, Show (ButtonCssClass (HandlerSite m))
|
|
||||||
, RenderMessage (HandlerSite m) ButtonMessage
|
|
||||||
, MonadHandler m
|
, MonadHandler m
|
||||||
) => [a] -> FieldSettings (HandlerSite m) -> AForm m [Maybe a]
|
) => [a] -> FieldSettings (HandlerSite m) -> AForm m [Maybe a]
|
||||||
combinedButtonField bs FieldSettings{..} = formToAForm $ do
|
combinedButtonField bs FieldSettings{..} = formToAForm $ do
|
||||||
@ -280,8 +276,6 @@ combinedButtonField bs FieldSettings{..} = formToAForm $ do
|
|||||||
|
|
||||||
combinedButtonFieldF :: forall m a.
|
combinedButtonFieldF :: forall m a.
|
||||||
( Button (HandlerSite m) a
|
( Button (HandlerSite m) a
|
||||||
, Show (ButtonCssClass (HandlerSite m))
|
|
||||||
, RenderMessage (HandlerSite m) ButtonMessage
|
|
||||||
, Finite a
|
, Finite a
|
||||||
, MonadHandler m
|
, MonadHandler m
|
||||||
) => FieldSettings (HandlerSite m) -> AForm m [Maybe a]
|
) => FieldSettings (HandlerSite m) -> AForm m [Maybe a]
|
||||||
@ -298,26 +292,22 @@ disambiguateButtons = traverseAForm $ \case
|
|||||||
|
|
||||||
combinedButtonField_ :: forall a m.
|
combinedButtonField_ :: forall a m.
|
||||||
( Button (HandlerSite m) a
|
( Button (HandlerSite m) a
|
||||||
, Show (ButtonCssClass (HandlerSite m))
|
|
||||||
, RenderMessage (HandlerSite m) ButtonMessage
|
|
||||||
, MonadHandler m
|
, MonadHandler m
|
||||||
) => [a] -> FieldSettings (HandlerSite m) -> AForm m ()
|
) => [a] -> FieldSettings (HandlerSite m) -> AForm m ()
|
||||||
combinedButtonField_ = (void .) . combinedButtonField
|
combinedButtonField_ = (void .) . combinedButtonField
|
||||||
|
|
||||||
combinedButtonFieldF_ :: forall m a p.
|
combinedButtonFieldF_ :: forall m a p.
|
||||||
( Button (HandlerSite m) a
|
( Button (HandlerSite m) a
|
||||||
, Show (ButtonCssClass (HandlerSite m))
|
|
||||||
, RenderMessage (HandlerSite m) ButtonMessage
|
|
||||||
, MonadHandler m
|
, MonadHandler m
|
||||||
, Finite a
|
, Finite a
|
||||||
) => p a -> FieldSettings (HandlerSite m) -> AForm m ()
|
) => p a -> FieldSettings (HandlerSite m) -> AForm m ()
|
||||||
combinedButtonFieldF_ _ = void . combinedButtonFieldF @m @a
|
combinedButtonFieldF_ _ = void . combinedButtonFieldF @m @a
|
||||||
|
|
||||||
submitButton :: (Button (HandlerSite m) SubmitButton, Show (ButtonCssClass (HandlerSite m)), MonadHandler m, RenderMessage (HandlerSite m) ButtonMessage) => AForm m ()
|
submitButton :: (Button (HandlerSite m) ButtonSubmit, MonadHandler m) => AForm m ()
|
||||||
submitButton = combinedButtonFieldF_ (Proxy @SubmitButton) ""
|
submitButton = combinedButtonFieldF_ (Proxy @ButtonSubmit) ""
|
||||||
|
|
||||||
autosubmitButton :: (Button (HandlerSite m) SubmitButton, Show (ButtonCssClass (HandlerSite m)), MonadHandler m, RenderMessage (HandlerSite m) ButtonMessage) => AForm m ()
|
autosubmitButton :: (Button (HandlerSite m) ButtonSubmit, MonadHandler m) => AForm m ()
|
||||||
autosubmitButton = combinedButtonFieldF_ (Proxy @SubmitButton) $ "" & addAutosubmit
|
autosubmitButton = combinedButtonFieldF_ (Proxy @ButtonSubmit) $ "" & addAutosubmit
|
||||||
|
|
||||||
-------------------
|
-------------------
|
||||||
-- Custom Fields --
|
-- Custom Fields --
|
||||||
|
|||||||
46
src/Utils/Sheet.hs
Normal file
46
src/Utils/Sheet.hs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
module Utils.Sheet where
|
||||||
|
|
||||||
|
import Import.NoFoundation
|
||||||
|
import qualified Database.Esqueleto as E
|
||||||
|
|
||||||
|
|
||||||
|
-- DB Queries for Sheets that are used in several places
|
||||||
|
|
||||||
|
sheetCurrent :: MonadIO m => TermId -> SchoolId -> CourseShorthand -> SqlReadT m (Maybe SheetName)
|
||||||
|
sheetCurrent tid ssh csh = do
|
||||||
|
now <- liftIO getCurrentTime
|
||||||
|
sheets <- E.select . E.from $ \(course `E.InnerJoin` sheet) -> do
|
||||||
|
E.on $ sheet E.^. SheetCourse E.==. course E.^. CourseId
|
||||||
|
E.where_ $ sheet E.^. SheetActiveTo E.>. E.val now
|
||||||
|
E.&&. sheet E.^. SheetActiveFrom E.<=. E.val now
|
||||||
|
E.&&. course E.^. CourseTerm E.==. E.val tid
|
||||||
|
E.&&. course E.^. CourseSchool E.==. E.val ssh
|
||||||
|
E.&&. course E.^. CourseShorthand E.==. E.val csh
|
||||||
|
E.orderBy [E.asc $ sheet E.^. SheetActiveTo]
|
||||||
|
E.limit 1
|
||||||
|
return $ sheet E.^. SheetName
|
||||||
|
return $ case sheets of
|
||||||
|
[] -> Nothing
|
||||||
|
[E.Value shn] -> Just shn
|
||||||
|
_ -> error "SQL Query with limit 1 returned more than one result"
|
||||||
|
|
||||||
|
|
||||||
|
sheetOldUnassigned :: MonadIO m => TermId -> SchoolId -> CourseShorthand -> SqlReadT m (Maybe SheetName)
|
||||||
|
sheetOldUnassigned tid ssh csh = do
|
||||||
|
now <- liftIO getCurrentTime
|
||||||
|
sheets <- E.select . E.from $ \(course `E.InnerJoin` sheet) -> do
|
||||||
|
E.on $ sheet E.^. SheetCourse E.==. course E.^. CourseId
|
||||||
|
E.where_ $ sheet E.^. SheetActiveTo E.<=. E.val now
|
||||||
|
E.&&. course E.^. CourseTerm E.==. E.val tid
|
||||||
|
E.&&. course E.^. CourseSchool E.==. E.val ssh
|
||||||
|
E.&&. course E.^. CourseShorthand E.==. E.val csh
|
||||||
|
E.where_ . E.exists . E.from $ \submission ->
|
||||||
|
E.where_ $ submission E.^. SubmissionSheet E.==. sheet E.^. SheetId
|
||||||
|
E.&&. E.isNothing (submission E.^. SubmissionRatingBy)
|
||||||
|
E.orderBy [E.asc $ sheet E.^. SheetActiveTo]
|
||||||
|
E.limit 1
|
||||||
|
return $ sheet E.^. SheetName
|
||||||
|
return $ case sheets of
|
||||||
|
[] -> Nothing
|
||||||
|
[E.Value shn] -> Just shn
|
||||||
|
_ -> error "SQL Query with limit 1 returned more than one result"
|
||||||
@ -36,3 +36,5 @@
|
|||||||
^{modal "Klick mich für Content-Test" (Right "Test Inhalt für Modal")}
|
^{modal "Klick mich für Content-Test" (Right "Test Inhalt für Modal")}
|
||||||
<li>
|
<li>
|
||||||
^{modal "Email-Test" (Right emailWidget')}
|
^{modal "Email-Test" (Right emailWidget')}
|
||||||
|
<li>
|
||||||
|
^{visibleWidget False}
|
||||||
|
|||||||
@ -1,10 +1,9 @@
|
|||||||
<div .container>
|
<div .container>
|
||||||
<dl .deflist>
|
<dl .deflist>
|
||||||
$maybe school <- schoolMB
|
<dt .deflist__dt>Fakultät/Institut
|
||||||
<dt .deflist__dt>Fakultät/Institut
|
<dd .deflist__dd>
|
||||||
<dd .deflist__dd>
|
<div>
|
||||||
<div>
|
#{schoolName}
|
||||||
#{schoolName school}
|
|
||||||
|
|
||||||
$maybe descr <- courseDescription course
|
$maybe descr <- courseDescription course
|
||||||
<dt .deflist__dt>_{MsgCourseDescription}
|
<dt .deflist__dt>_{MsgCourseDescription}
|
||||||
@ -33,20 +32,27 @@ $# $if NTop (Just 0) < NTop (courseCapacity course)
|
|||||||
#{participants}
|
#{participants}
|
||||||
$maybe capacity <- courseCapacity course
|
$maybe capacity <- courseCapacity course
|
||||||
\ von #{capacity}
|
\ von #{capacity}
|
||||||
$maybe regFrom <- mRegFrom
|
$maybe regFrom <- mRegFrom
|
||||||
<dt .deflist__dt>Anmeldezeitraum
|
<dt .deflist__dt>Anmeldezeitraum
|
||||||
<dd .deflist__dd>
|
<dd .deflist__dd>
|
||||||
|
<div>
|
||||||
|
Ab #{regFrom}
|
||||||
|
$maybe regTo <- mRegTo
|
||||||
|
\ bis #{regTo}
|
||||||
|
$maybe dereg <- mDereg
|
||||||
<div>
|
<div>
|
||||||
Ab #{regFrom}
|
\ <em>Achtung:</em>
|
||||||
$maybe regTo <- mRegTo
|
\ Abmeldung nur bis #{dereg} erlaubt.
|
||||||
\ bis #{regTo}
|
$if registrationOpen || isJust mRegAt
|
||||||
$if registrationOpen
|
|
||||||
<dt .deflist__dt>
|
<dt .deflist__dt>
|
||||||
<dd .deflist__dd>
|
<dd .deflist__dd>
|
||||||
<div .course__registration>
|
<div .course__registration>
|
||||||
<form method=post action=@{CourseR tid ssh csh CRegisterR} enctype=#{regEnctype}>
|
$if registrationOpen
|
||||||
$# regWidget is defined through templates/widgets/registerForm
|
<form method=post action=@{CourseR tid ssh csh CRegisterR} enctype=#{regEnctype}>
|
||||||
^{regWidget}
|
$# regWidget is defined through templates/widgets/registerForm
|
||||||
|
^{regWidget}
|
||||||
|
$maybe date <- mRegAt
|
||||||
|
_{MsgRegisteredSince date}
|
||||||
<dt .deflist__dt>
|
<dt .deflist__dt>
|
||||||
Material
|
Material
|
||||||
<dd .deflist__dd>
|
<dd .deflist__dd>
|
||||||
|
|||||||
@ -1,3 +1,2 @@
|
|||||||
<div .container>
|
<div .container>
|
||||||
<div .scrolltable>
|
^{coursesTable}
|
||||||
^{coursesTable}
|
|
||||||
|
|||||||
@ -335,6 +335,8 @@ input[type="button"].btn-info:hover,
|
|||||||
/* SCROLLTABLE */
|
/* SCROLLTABLE */
|
||||||
.scrolltable {
|
.scrolltable {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
box-shadow: 0 0 3px 0 var(--color-grey);
|
||||||
|
margin-bottom: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 425px) {
|
@media (max-width: 425px) {
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
.checkbox {
|
.checkbox {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-left: 5px;
|
margin-left: 7px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -234,7 +234,8 @@ input[type="checkbox"]:checked::after {
|
|||||||
height: 24px;
|
height: 24px;
|
||||||
width: 24px;
|
width: 24px;
|
||||||
background-color: #f3f3f3;
|
background-color: #f3f3f3;
|
||||||
box-shadow: inset 0 1px 2px 1px rgba(50,50,50,.05);
|
box-shadow: inset 0 1px 2px 1px rgba(50, 50, 50, 0.05);
|
||||||
|
border: 2px solid var(--color-primary);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
color: white;
|
color: white;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@ -242,39 +243,43 @@ input[type="checkbox"]:checked::after {
|
|||||||
|
|
||||||
label::before,
|
label::before,
|
||||||
label::after {
|
label::after {
|
||||||
content: '';
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 11px;
|
|
||||||
left: 3px;
|
|
||||||
display: block;
|
display: block;
|
||||||
width: 18px;
|
top: 12px;
|
||||||
|
left: 8px;
|
||||||
height: 2px;
|
height: 2px;
|
||||||
|
width: 8px;
|
||||||
background-color: var(--color-font);
|
background-color: var(--color-font);
|
||||||
transition: all .2s;
|
|
||||||
transform: scale(0.5, 0.1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
:checked + label {
|
:checked + label {
|
||||||
background-color: var(--color-primary);
|
background-color: var(--color-primary);
|
||||||
text-decoration: underline;
|
}
|
||||||
|
|
||||||
|
:checked + label::before,
|
||||||
|
:checked + label::after {
|
||||||
|
content: '';
|
||||||
}
|
}
|
||||||
|
|
||||||
:checked + label::before {
|
:checked + label::before {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
transform: scale(1, 1) rotate(45deg);
|
transform: rotate(45deg);
|
||||||
|
left: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:checked + label::after {
|
:checked + label::after {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
transform: scale(1, 1) rotate(-45deg);
|
transform: rotate(-45deg);
|
||||||
|
top: 11px;
|
||||||
|
width: 13px;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.radio label::before {
|
[disabled] + label {
|
||||||
transform: scale(0.01, 0.01) rotate(45deg);
|
pointer-events: none;
|
||||||
}
|
border: none;
|
||||||
.radio label::after {
|
opacity: 0.6;
|
||||||
transform: scale(0.01, 0.01) rotate(-45deg);
|
filter: grayscale(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.radio::before {
|
.radio::before {
|
||||||
|
|||||||
@ -3,6 +3,6 @@ $newline never
|
|||||||
<form method=GET action=#{filterAction} enctype=#{filterEnctype}>
|
<form method=GET action=#{filterAction} enctype=#{filterEnctype}>
|
||||||
^{filterWgdt}
|
^{filterWgdt}
|
||||||
<button>
|
<button>
|
||||||
^{label BtnSubmit}
|
^{btnLabel BtnSubmit}
|
||||||
<section>
|
<section>
|
||||||
^{scrolltable}
|
^{scrolltable}
|
||||||
|
|||||||
@ -3,10 +3,17 @@ $if null rows && (dbsEmptyStyle == DBESNoHeading)
|
|||||||
_{dbsEmptyMessage}
|
_{dbsEmptyMessage}
|
||||||
$else
|
$else
|
||||||
^{table}
|
^{table}
|
||||||
|
|
||||||
|
<div .table-footer>
|
||||||
|
<div .table__row-count>
|
||||||
|
_{MsgRowCount rowCount}
|
||||||
|
$# Since the current pagesize is always a member of pagesizeOptions we don't need to check `pageCount > 1`
|
||||||
|
$if toEnum (fromIntegral rowCount) > minimum (pagesizeOptions referencePagesize)
|
||||||
|
<form .pagesize ##{wIdent "pagesize-form"} method=GET enctype=#{pagesizeEnc} action=#{rawAction}>
|
||||||
|
^{pagesizeWdgt}
|
||||||
|
|
||||||
$if pageCount > 1
|
$if pageCount > 1
|
||||||
<div .pagination>
|
<div .pagination>
|
||||||
<form .pagesize method=GET enctype=#{pagesizeEnc} action=#{rawAction}>
|
|
||||||
^{pagesizeWdgt}
|
|
||||||
<ul ##{wIdent "pagination"} .pages>
|
<ul ##{wIdent "pagination"} .pages>
|
||||||
$forall p <- pageNumbers
|
$forall p <- pageNumbers
|
||||||
<li .page-link :p == psPage:.current>
|
<li .page-link :p == psPage:.current>
|
||||||
|
|||||||
@ -1,94 +1,148 @@
|
|||||||
(function collonadeClosure() {
|
(function collonadeClosure() {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
document.addEventListener('setup', function DOMContentLoaded(e) {
|
window.utils = window.utils || {};
|
||||||
|
|
||||||
console.log('dbtable', e);
|
window.utils.asyncTable = function(wrapper) {
|
||||||
|
|
||||||
if (e.detail.module && e.detail.module !== 'dbtable')
|
var tableIdent = #{String dbtIdent};
|
||||||
return;
|
var shortCircuitHeader = #{String (toPathPiece HeaderDBTableShortcircuit)};
|
||||||
|
|
||||||
function setupAsync(wrapper) {
|
var ths = [];
|
||||||
|
var pagination;
|
||||||
|
var pagesizeForm;
|
||||||
|
|
||||||
var table = wrapper.querySelector('#' + #{String dbtIdent});
|
function init() {
|
||||||
if (!table)
|
var table = wrapper.querySelector('#' + tableIdent);
|
||||||
|
if (!table) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
var ths = Array.from(table.querySelectorAll('th.sortable'));
|
|
||||||
var pagination = wrapper.querySelector('#' + #{String dbtIdent} + '-pagination');
|
|
||||||
|
|
||||||
|
ths = Array.from(table.querySelectorAll('th.sortable'));
|
||||||
|
pagination = wrapper.querySelector('#' + tableIdent + '-pagination');
|
||||||
|
pagesizeForm = wrapper.querySelector('#' + tableIdent + '-pagesize-form');
|
||||||
|
|
||||||
|
setupListeners();
|
||||||
|
wrapper.classList.add('js-initialized');
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupListeners() {
|
||||||
ths.forEach(function(th) {
|
ths.forEach(function(th) {
|
||||||
th.addEventListener('click', clickHandler);
|
th.addEventListener('click', clickHandler);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (pagination) {
|
if (pagination) {
|
||||||
Array.from(pagination.querySelectorAll('.page-link'))
|
var pageLinks = Array.from(pagination.querySelectorAll('.page-link'));
|
||||||
.forEach(function(p) {
|
pageLinks.forEach(function(p) {
|
||||||
p.addEventListener('click', clickHandler);
|
p.addEventListener('click', clickHandler);
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function clickHandler(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
var url = new URL(window.location.origin + window.location.pathname + getClickDestination(this));
|
|
||||||
updateTableFrom(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getClickDestination(el) {
|
|
||||||
console.log(el);
|
|
||||||
if (!el.querySelector('a')) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return el.querySelector('a').getAttribute('href');
|
|
||||||
}
|
|
||||||
|
|
||||||
// fetches new sorted table from url with params and replaces contents of current table
|
|
||||||
function updateTableFrom(url) {
|
|
||||||
fetch(url, {
|
|
||||||
credentials: 'same-origin',
|
|
||||||
headers: {
|
|
||||||
'Accept': 'text/html',
|
|
||||||
#{String (toPathPiece HeaderDBTableShortcircuit)}: #{String dbtIdent}
|
|
||||||
}
|
|
||||||
}).then(function(response) {
|
|
||||||
if (!response.ok) {
|
|
||||||
throw ('Looks like there was a problem fetching ' + url.toString() + '. Status Code: ' + response.status);
|
|
||||||
}
|
|
||||||
return response.text();
|
|
||||||
}).then(function(data) {
|
|
||||||
// remove listeners
|
|
||||||
ths.forEach(function(th) {
|
|
||||||
th.removeEventListener('click', clickHandler);
|
|
||||||
});
|
|
||||||
|
|
||||||
// replace contents of table body
|
|
||||||
wrapper.innerHTML = data;
|
|
||||||
|
|
||||||
// set up async functionality again
|
|
||||||
wrapper.classList.remove("js-initialized");
|
|
||||||
document.dispatchEvent(new CustomEvent('setup', {
|
|
||||||
detail: { scope: wrapper },
|
|
||||||
bubbles: true,
|
|
||||||
cancelable: true
|
|
||||||
}));
|
|
||||||
// table.querySelector('tbody').innerHTML = data;
|
|
||||||
}).catch(function(err) {
|
|
||||||
console.error(err);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
wrapper.classList.add("js-initialized");
|
if (pagesizeForm) {
|
||||||
|
var pagesizeSelect = pagesizeForm.querySelector('[name=' + tableIdent + '-pagesize]')
|
||||||
|
pagesizeSelect.addEventListener('change', changeHandler);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var selector = '#' + #{String $ dbtIdent} + '-table-wrapper:not(.js-initialized)';
|
function removeListeners() {
|
||||||
var wrapperEl = e.detail.scope.querySelector(selector);
|
ths.forEach(function(th) {
|
||||||
if (wrapperEl)
|
th.removeEventListener('click', clickHandler);
|
||||||
setupAsync(wrapperEl);
|
});
|
||||||
else if (e.detail.scope.matches(selector))
|
|
||||||
setupAsync(e.detail.scope);
|
if (pagination) {
|
||||||
});
|
var pageLinks = Array.from(pagination.querySelectorAll('.page-link'));
|
||||||
|
pageLinks.forEach(function(p) {
|
||||||
|
p.removeEventListener('click', clickHandler);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pagesizeForm) {
|
||||||
|
var pagesizeSelect = pagesizeForm.querySelector('[name=' + tableIdent + '-pagesize]')
|
||||||
|
pagesizeSelect.removeEventListener('change', changeHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clickHandler(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
var url = new URL(window.location.origin + window.location.pathname + getClickDestination(this));
|
||||||
|
updateTableFrom(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getClickDestination(el) {
|
||||||
|
if (!el.querySelector('a')) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return el.querySelector('a').getAttribute('href');
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeHandler(event) {
|
||||||
|
var currentTableUrl = wrapper.dataset.currentUrl || window.location.href;
|
||||||
|
var url = getUrlWithUpdatedPagesize(currentTableUrl, event.target.value);
|
||||||
|
url = getUrlWithResetPagenumber(url);
|
||||||
|
updateTableFrom(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUrlWithUpdatedPagesize(url, pagesize) {
|
||||||
|
if (url.indexOf('pagesize') >= 0) {
|
||||||
|
return url.replace(/pagesize=(\d+)/, 'pagesize=' + pagesize);
|
||||||
|
} else if (url.indexOf('?') >= 0) {
|
||||||
|
return url += '&' + tableIdent + '-pagesize=' + pagesize;
|
||||||
|
}
|
||||||
|
|
||||||
|
return url += '?' + tableIdent + '-pagesize=' + pagesize;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUrlWithResetPagenumber(url) {
|
||||||
|
return url.replace(/-page=\d+/, '-page=0');
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateWrapperContents(newHtml) {
|
||||||
|
wrapper.innerHTML = newHtml;
|
||||||
|
wrapper.classList.remove("js-initialized");
|
||||||
|
|
||||||
|
// setup the wrapper and its components to behave async again
|
||||||
|
window.utils.asyncTable(wrapper);
|
||||||
|
|
||||||
|
// make sure to hide any new submit buttons
|
||||||
|
document.dispatchEvent(new CustomEvent('setup', {
|
||||||
|
detail: {
|
||||||
|
scope: wrapper,
|
||||||
|
module: 'autoSubmit'
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetches new sorted table from url with params and replaces contents of current table
|
||||||
|
function updateTableFrom(url) {
|
||||||
|
|
||||||
|
fetch(url, {
|
||||||
|
credentials: 'same-origin',
|
||||||
|
headers: {
|
||||||
|
'Accept': 'text/html',
|
||||||
|
[shortCircuitHeader]: tableIdent
|
||||||
|
}
|
||||||
|
}).then(function(response) {
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Looks like there was a problem fetching ' + url + '. Status Code: ' + response.status);
|
||||||
|
}
|
||||||
|
return response.text();
|
||||||
|
}).then(function(data) {
|
||||||
|
wrapper.dataset.currentUrl = url;
|
||||||
|
removeListeners();
|
||||||
|
updateWrapperContents(data);
|
||||||
|
}).catch(function(err) {
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
init();
|
||||||
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
document.dispatchEvent(new CustomEvent('setup', { detail: { scope: document.body, module: 'dbtable' }, bubbles: true, cancelable: true }));
|
var selector = '#' + #{String $ dbtIdent} + '-table-wrapper:not(.js-initialized)';
|
||||||
|
var wrapper = document.querySelector(selector);
|
||||||
|
if (wrapper) {
|
||||||
|
window.utils.asyncTable(wrapper);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,24 +1,29 @@
|
|||||||
|
/* TABLE FOOTER */
|
||||||
|
.table-footer {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row-reverse;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
/* PAGINATION */
|
/* PAGINATION */
|
||||||
.pagination {
|
.pagination {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
display: flex;
|
overflow: auto;
|
||||||
flex-direction: row;
|
|
||||||
|
|
||||||
.pagesize {
|
|
||||||
float: left;
|
|
||||||
flex-grow: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pages {
|
.pages {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
flex-grow: 1;
|
white-space: nowrap;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
.page-link {
|
.page-link {
|
||||||
margin: 0 7px;
|
margin-top: 7px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
background-color: var(--color-grey);
|
background-color: var(--color-grey);
|
||||||
|
|
||||||
|
+ .page-link {
|
||||||
|
margin-left: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: var(--color-lightwhite);
|
color: var(--color-lightwhite);
|
||||||
padding: 7px 13px;
|
padding: 7px 13px;
|
||||||
@ -42,10 +47,6 @@
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,16 +17,7 @@
|
|||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
document.addEventListener('setup', function(e) {
|
|
||||||
if (e.detail.module && e.detail.module !== 'asidenav')
|
|
||||||
return;
|
|
||||||
|
|
||||||
var asidenavEl = e.detail.scope.querySelector('.main__aside');
|
|
||||||
|
|
||||||
window.utils.aside(asidenavEl);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
document.dispatchEvent(new CustomEvent('setup', { detail: { scope: document.body, module: 'asidenav' }, bubbles: true, cancelable: true }))
|
var asidenavEl = document.querySelector('.main__aside');
|
||||||
|
window.utils.aside(asidenavEl);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,6 +3,7 @@ $newline never
|
|||||||
$case formLayout
|
$case formLayout
|
||||||
$of FormDBTablePagesize
|
$of FormDBTablePagesize
|
||||||
$forall view <- fieldViews
|
$forall view <- fieldViews
|
||||||
|
<label .form-group__label.label-pagesize for=#{fvId view}>#{fvLabel view}
|
||||||
^{fvInput view}
|
^{fvInput view}
|
||||||
$of _
|
$of _
|
||||||
$forall view <- fieldViews
|
$forall view <- fieldViews
|
||||||
|
|||||||
@ -55,8 +55,6 @@
|
|||||||
|
|
||||||
function addEventListeners() {
|
function addEventListeners() {
|
||||||
fields.forEach(function(field) {
|
fields.forEach(function(field) {
|
||||||
console.log('interactiveFieldset', 'addEventListeners', field);
|
|
||||||
|
|
||||||
field.condEl.addEventListener('input', updateFields)
|
field.condEl.addEventListener('input', updateFields)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -72,8 +70,6 @@ document.addEventListener('setup', function(e) {
|
|||||||
if (e.detail.module && e.detail.module !== 'showHide')
|
if (e.detail.module && e.detail.module !== 'showHide')
|
||||||
return;
|
return;
|
||||||
|
|
||||||
console.log('form setup', e.detail.scope);
|
|
||||||
|
|
||||||
var forms = e.detail.scope.querySelectorAll('form');
|
var forms = e.detail.scope.querySelectorAll('form');
|
||||||
Array.from(forms).forEach(function(form) {
|
Array.from(forms).forEach(function(form) {
|
||||||
// auto reactiveButton submit-buttons with required fields
|
// auto reactiveButton submit-buttons with required fields
|
||||||
@ -114,11 +110,9 @@ document.addEventListener('setup', function(e) {
|
|||||||
var target = ancestor || elem;
|
var target = ancestor || elem;
|
||||||
|
|
||||||
target.classList.add('hidden');
|
target.classList.add('hidden');
|
||||||
} else if (elem.form) {
|
|
||||||
elem.addEventListener('change', function () { elem.form.submit() })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
elem.classList.add('.js-initalized');
|
elem.classList.add('js-initialized');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -11,14 +11,32 @@ fieldset {
|
|||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[data-autosubmit][type="submit"] {
|
||||||
|
animation: fade-in 500ms ease-in-out backwards;
|
||||||
|
animation-delay: 500ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-in {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.hidden {
|
.hidden {
|
||||||
visibility: hidden;
|
visibility: hidden !important;
|
||||||
height: 0;
|
height: 0 !important;
|
||||||
opacity: 0;
|
width: 0 !important;
|
||||||
margin: 0;
|
opacity: 0 !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
min-width: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.select--pagesize {
|
.select--pagesize {
|
||||||
width: 5em;
|
width: 5em;
|
||||||
min-width: 75px;
|
min-width: 75px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.label-pagesize {
|
||||||
|
margin-right: 13px;
|
||||||
|
}
|
||||||
|
|||||||
@ -9,34 +9,35 @@ $# rowWdgts :: Liste von Widgets für jede Zeile (Normal,Bonus,KeineWert
|
|||||||
$# --
|
$# --
|
||||||
<div>
|
<div>
|
||||||
<h3>_{MsgSummaryTitle} _{title $ getSum $ numSheets $ sumSummaries}
|
<h3>_{MsgSummaryTitle} _{title $ getSum $ numSheets $ sumSummaries}
|
||||||
<table .table .table--striped>
|
<div .scrolltable>
|
||||||
<tr .table__row .table__row--head>
|
<table .table .table--striped>
|
||||||
<th>
|
<tr .table__row .table__row--head>
|
||||||
$# empty cell for row headers
|
<th>
|
||||||
$maybe _ <- hasMarkedPasses
|
$# empty cell for row headers
|
||||||
<th .table__th colspan=2>_{MsgCorrected}
|
|
||||||
$maybe _ <- hasPasses
|
|
||||||
<th .table__th>_{MsgSheetGradingPassing'}
|
|
||||||
$maybe _ <- hasMarkedPoints
|
|
||||||
<th .table__th colspan=2>_{MsgCorrected}
|
|
||||||
$maybe _ <- hasPoints
|
|
||||||
<th .table__th>_{MsgSheetGradingPoints'}
|
|
||||||
<th .table__th>_{MsgSheetGradingCount'}
|
|
||||||
$# Number of Sheet/Submissions used for calculating maximum passes/points
|
|
||||||
$forall row <- rowWdgts
|
|
||||||
^{row}
|
|
||||||
$maybe nrNoGrade <- positiveSum $ numNotGraded
|
|
||||||
<tr .table__row>
|
|
||||||
<th .table__th>_{MsgSheetTypeNotGraded}
|
|
||||||
$maybe _ <- hasMarkedPasses
|
$maybe _ <- hasMarkedPasses
|
||||||
<td colspan=2>
|
<th .table__th colspan=2>_{MsgCorrected}
|
||||||
$maybe _ <- hasPasses
|
$maybe _ <- hasPasses
|
||||||
<td .table__td>
|
<th .table__th>_{MsgSheetGradingPassing'}
|
||||||
$maybe _ <- hasMarkedPoints
|
$maybe _ <- hasMarkedPoints
|
||||||
<td .table__td colspan=2>
|
<th .table__th colspan=2>_{MsgCorrected}
|
||||||
$maybe _ <- hasPoints
|
$maybe _ <- hasPoints
|
||||||
<td .table__td>
|
<th .table__th>_{MsgSheetGradingPoints'}
|
||||||
<td .table__td>#{nrNoGrade}
|
<th .table__th>_{MsgSheetGradingCount'}
|
||||||
|
$# Number of Sheet/Submissions used for calculating maximum passes/points
|
||||||
|
$forall row <- rowWdgts
|
||||||
|
^{row}
|
||||||
|
$maybe nrNoGrade <- positiveSum $ numNotGraded
|
||||||
|
<tr .table__row>
|
||||||
|
<th .table__th>_{MsgSheetTypeNotGraded}
|
||||||
|
$maybe _ <- hasMarkedPasses
|
||||||
|
<td colspan=2>
|
||||||
|
$maybe _ <- hasPasses
|
||||||
|
<td .table__td>
|
||||||
|
$maybe _ <- hasMarkedPoints
|
||||||
|
<td .table__td colspan=2>
|
||||||
|
$maybe _ <- hasPoints
|
||||||
|
<td .table__td>
|
||||||
|
<td .table__td>#{nrNoGrade}
|
||||||
$maybe _ <- positiveSum $ bonusSummary ^. _numSheets
|
$maybe _ <- positiveSum $ bonusSummary ^. _numSheets
|
||||||
<p>_{MsgSheetTypeInfoBonus} #
|
<p>_{MsgSheetTypeInfoBonus} #
|
||||||
$maybe _ <- positiveSum $ bonusSummary ^. _achievedPoints
|
$maybe _ <- positiveSum $ bonusSummary ^. _achievedPoints
|
||||||
|
|||||||
@ -5,4 +5,3 @@ $maybe secretView <- msecretView
|
|||||||
^{fvInput secretView}
|
^{fvInput secretView}
|
||||||
$# Always display register/deregister button
|
$# Always display register/deregister button
|
||||||
^{fvInput btnView}
|
^{fvInput btnView}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user