feat(users): sex
This commit is contained in:
parent
66460487f5
commit
c2a8381278
@ -130,6 +130,7 @@ user-defaults:
|
|||||||
time-format: "%R"
|
time-format: "%R"
|
||||||
download-files: false
|
download-files: false
|
||||||
warning-days: 1209600
|
warning-days: 1209600
|
||||||
|
show-sex: false
|
||||||
|
|
||||||
# During central allocations lecturer-given ratings of applications (as
|
# During central allocations lecturer-given ratings of applications (as
|
||||||
# ExamGrades) are combined with a central priority.
|
# ExamGrades) are combined with a central priority.
|
||||||
|
|||||||
@ -687,6 +687,7 @@ CampusUserInvalidTitle: Konnte anhand des Campus-Logins keinen akademischen Tite
|
|||||||
CampusUserInvalidMatriculation: Konnte anhand des Campus-Logins keine Matrikelnummer ermitteln
|
CampusUserInvalidMatriculation: Konnte anhand des Campus-Logins keine Matrikelnummer ermitteln
|
||||||
CampusUserInvalidFeaturesOfStudy parseErr@Text: Konnte anhand des Campus-Logins keine Studiengänge ermitteln
|
CampusUserInvalidFeaturesOfStudy parseErr@Text: Konnte anhand des Campus-Logins keine Studiengänge ermitteln
|
||||||
CampusUserInvalidAssociatedSchools parseErr@Text: Konnte anhand des Campus-Logins keine Institute ermitteln
|
CampusUserInvalidAssociatedSchools parseErr@Text: Konnte anhand des Campus-Logins keine Institute ermitteln
|
||||||
|
CampusUserInvalidSex: Konnte anhand des Campus-Logins kein Geschlecht ermitteln
|
||||||
|
|
||||||
CorrectorNormal: Normal
|
CorrectorNormal: Normal
|
||||||
CorrectorMissing: Abwesend
|
CorrectorMissing: Abwesend
|
||||||
@ -1559,6 +1560,7 @@ CsvColumnExamUserCourseNote: Notizen zum Teilnehmer
|
|||||||
|
|
||||||
CsvColumnUserName: Voller Name des Teilnehmers
|
CsvColumnUserName: Voller Name des Teilnehmers
|
||||||
CsvColumnUserMatriculation: Matrikelnummer des Teilnehmers
|
CsvColumnUserMatriculation: Matrikelnummer des Teilnehmers
|
||||||
|
CsvColumnUserSex: Geschlecht
|
||||||
CsvColumnUserEmail: E-Mail Addresse des Teilnehmers
|
CsvColumnUserEmail: E-Mail Addresse des Teilnehmers
|
||||||
CsvColumnUserStudyFeatures: Alle aktiven Studiendaten des Teilnehmers als Semikolon (;) separierte Liste
|
CsvColumnUserStudyFeatures: Alle aktiven Studiendaten des Teilnehmers als Semikolon (;) separierte Liste
|
||||||
CsvColumnUserField: Studienfach, mit dem der Teilnehmer seine Kursanmeldung assoziiert hat
|
CsvColumnUserField: Studienfach, mit dem der Teilnehmer seine Kursanmeldung assoziiert hat
|
||||||
@ -1769,7 +1771,9 @@ SchoolExamOffice: Prüfungsamt
|
|||||||
ApplicationEditTip: Während des Bewerbungszeitraums können eigene Bewerbungen beliebig angepasst und auch wieder zurückgezogen werden.
|
ApplicationEditTip: Während des Bewerbungszeitraums können eigene Bewerbungen beliebig angepasst und auch wieder zurückgezogen werden.
|
||||||
|
|
||||||
UserLdapSync: LDAP-Synchronisieren
|
UserLdapSync: LDAP-Synchronisieren
|
||||||
|
AllUsersLdapSync: Alle LDAP-Synchronisieren
|
||||||
SynchroniseLdapUserQueued n@Int: LDAP-Synchronisation von #{n} #{pluralDE n "Benutzer" "Benutzern"} angestoßen
|
SynchroniseLdapUserQueued n@Int: LDAP-Synchronisation von #{n} #{pluralDE n "Benutzer" "Benutzern"} angestoßen
|
||||||
|
SynchroniseLdapAllUsersQueued: LDAP-Synchronisation von allen Benutzern angestoßen
|
||||||
UserHijack: Sitzung übernehmen
|
UserHijack: Sitzung übernehmen
|
||||||
|
|
||||||
MailSubjectAllocationStaffRegister allocation@AllocationName: Sie können nun Kurse für die Zentralameldung „#{allocation}“ registrieren
|
MailSubjectAllocationStaffRegister allocation@AllocationName: Sie können nun Kurse für die Zentralameldung „#{allocation}“ registrieren
|
||||||
@ -1820,6 +1824,7 @@ AdminUserDisplayEmail: Anzeige-E-Mail
|
|||||||
AdminUserIdent: Identifikation
|
AdminUserIdent: Identifikation
|
||||||
AdminUserAuth: Authentifizierung
|
AdminUserAuth: Authentifizierung
|
||||||
AdminUserMatriculation: Matrikelnummer
|
AdminUserMatriculation: Matrikelnummer
|
||||||
|
AdminUserSex: Geschlecht
|
||||||
AuthKindLDAP: Campus-Kennung
|
AuthKindLDAP: Campus-Kennung
|
||||||
AuthKindPWHash: Uni2work-Kennung
|
AuthKindPWHash: Uni2work-Kennung
|
||||||
UserAdded: Benutzer erfolgreich angelegt
|
UserAdded: Benutzer erfolgreich angelegt
|
||||||
@ -1945,4 +1950,18 @@ CourseEventDeleteQuestion: Wollen Sie den unten aufgeführten Termin wirklich l
|
|||||||
CourseEventDeleted: Kurstermin erfolgreich gelöscht
|
CourseEventDeleted: Kurstermin erfolgreich gelöscht
|
||||||
|
|
||||||
UserSimplifiedFeaturesOfStudyCsv: Vereinfachte Studiengangsdaten
|
UserSimplifiedFeaturesOfStudyCsv: Vereinfachte Studiengangsdaten
|
||||||
UserSimplifiedFeaturesOfStudyCsvTip: Sollen Abschluss, Studiengang und Semester zur einfacheren Verarbeitung als separate Spalten exportiert werden? Es wird dann nur jeweils das Fach exportiert, dass der Student bei der Anmeldung ausgewählt hat.
|
UserSimplifiedFeaturesOfStudyCsvTip: Sollen Abschluss, Studiengang und Semester zur einfacheren Verarbeitung als separate Spalten exportiert werden? Es wird dann nur jeweils das Fach exportiert, dass der Student bei der Anmeldung ausgewählt hat.
|
||||||
|
|
||||||
|
Sex: Geschlecht
|
||||||
|
SexNotKnown: Unbekannt
|
||||||
|
SexMale: Männlich
|
||||||
|
SexFemale: Weiblich
|
||||||
|
SexNotApplicable: Keine Angabe
|
||||||
|
|
||||||
|
ShortSexNotKnown: unb.
|
||||||
|
ShortSexMale: m
|
||||||
|
ShortSexFemale: w
|
||||||
|
ShortSexNotApplicable: k.A.
|
||||||
|
|
||||||
|
ShowSex: Geschlechter anderer Nutzer anzeigen
|
||||||
|
ShowSexTip: Sollen in Kursteilnehmer-Tabellen u.Ä. die Geschlechter der Nutzer angezeigt werden?
|
||||||
@ -32,6 +32,8 @@ User json -- Each Uni2work user has a corresponding row in this table; create
|
|||||||
notificationSettings NotificationSettings -- Bit-array for which events email notifications are requested by user; user-defined
|
notificationSettings NotificationSettings -- Bit-array for which events email notifications are requested by user; user-defined
|
||||||
warningDays NominalDiffTime default=1209600 -- timedistance to pending deadlines for homepage infos
|
warningDays NominalDiffTime default=1209600 -- timedistance to pending deadlines for homepage infos
|
||||||
csvOptions CsvOptions "default='{}'::jsonb"
|
csvOptions CsvOptions "default='{}'::jsonb"
|
||||||
|
sex Sex Maybe
|
||||||
|
showSex Bool default=false
|
||||||
UniqueAuthentication ident -- Column 'ident' can be used as a row-key in this table
|
UniqueAuthentication ident -- Column 'ident' can be used as a row-key in this table
|
||||||
UniqueEmail email -- Column 'email' can be used as a row-key in this table
|
UniqueEmail email -- Column 'email' can be used as a row-key in this table
|
||||||
deriving Show Eq Ord Generic -- Haskell-specific settings for runtime-value representing a row in memory
|
deriving Show Eq Ord Generic -- Haskell-specific settings for runtime-value representing a row in memory
|
||||||
|
|||||||
@ -7,7 +7,7 @@ module Auth.LDAP
|
|||||||
, ldapUserPrincipalName, ldapUserEmail, ldapUserDisplayName
|
, ldapUserPrincipalName, ldapUserEmail, ldapUserDisplayName
|
||||||
, ldapUserMatriculation, ldapUserFirstName, ldapUserSurname
|
, ldapUserMatriculation, ldapUserFirstName, ldapUserSurname
|
||||||
, ldapUserTitle, ldapUserStudyFeatures, ldapUserFieldName
|
, ldapUserTitle, ldapUserStudyFeatures, ldapUserFieldName
|
||||||
, ldapUserSchoolAssociation
|
, ldapUserSchoolAssociation, ldapSex
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Import.NoFoundation
|
import Import.NoFoundation
|
||||||
@ -59,7 +59,7 @@ findUser LdapConf{..} ldap ident retAttrs = fromMaybe [] <$> findM (assertM (not
|
|||||||
, Ldap.derefAliases Ldap.DerefAlways
|
, Ldap.derefAliases Ldap.DerefAlways
|
||||||
]
|
]
|
||||||
|
|
||||||
ldapUserPrincipalName, ldapUserEmail, ldapUserDisplayName, ldapUserMatriculation, ldapUserFirstName, ldapUserSurname, ldapUserTitle, ldapUserStudyFeatures, ldapUserFieldName, ldapUserSchoolAssociation :: Ldap.Attr
|
ldapUserPrincipalName, ldapUserEmail, ldapUserDisplayName, ldapUserMatriculation, ldapUserFirstName, ldapUserSurname, ldapUserTitle, ldapUserStudyFeatures, ldapUserFieldName, ldapUserSchoolAssociation, ldapSex :: Ldap.Attr
|
||||||
ldapUserPrincipalName = Ldap.Attr "userPrincipalName"
|
ldapUserPrincipalName = Ldap.Attr "userPrincipalName"
|
||||||
ldapUserEmail = Ldap.Attr "mail"
|
ldapUserEmail = Ldap.Attr "mail"
|
||||||
ldapUserDisplayName = Ldap.Attr "displayName"
|
ldapUserDisplayName = Ldap.Attr "displayName"
|
||||||
@ -70,6 +70,7 @@ ldapUserTitle = Ldap.Attr "title"
|
|||||||
ldapUserStudyFeatures = Ldap.Attr "dfnEduPersonFeaturesOfStudy"
|
ldapUserStudyFeatures = Ldap.Attr "dfnEduPersonFeaturesOfStudy"
|
||||||
ldapUserFieldName = Ldap.Attr "dfnEduPersonFieldOfStudyString"
|
ldapUserFieldName = Ldap.Attr "dfnEduPersonFieldOfStudyString"
|
||||||
ldapUserSchoolAssociation = Ldap.Attr "LMU-IFI-eduPersonOrgUnitDNString"
|
ldapUserSchoolAssociation = Ldap.Attr "LMU-IFI-eduPersonOrgUnitDNString"
|
||||||
|
ldapSex = Ldap.Attr "schacGender"
|
||||||
|
|
||||||
|
|
||||||
data CampusUserException = CampusUserLdapError LdapPoolError
|
data CampusUserException = CampusUserLdapError LdapPoolError
|
||||||
|
|||||||
@ -339,9 +339,13 @@ embedRenderMessage ''UniWorX ''SchoolFunction id
|
|||||||
embedRenderMessage ''UniWorX ''CsvPreset id
|
embedRenderMessage ''UniWorX ''CsvPreset id
|
||||||
embedRenderMessage ''UniWorX ''Quoting ("Csv" <>)
|
embedRenderMessage ''UniWorX ''Quoting ("Csv" <>)
|
||||||
embedRenderMessage ''UniWorX ''FavouriteReason id
|
embedRenderMessage ''UniWorX ''FavouriteReason id
|
||||||
|
embedRenderMessage ''UniWorX ''Sex id
|
||||||
|
|
||||||
embedRenderMessage ''UniWorX ''AuthenticationMode id
|
embedRenderMessage ''UniWorX ''AuthenticationMode id
|
||||||
|
|
||||||
|
newtype ShortSex = ShortSex Sex
|
||||||
|
embedRenderMessageVariant ''UniWorX ''ShortSex ("Short" <>)
|
||||||
|
|
||||||
newtype SheetTypeHeader = SheetTypeHeader SheetType
|
newtype SheetTypeHeader = SheetTypeHeader SheetType
|
||||||
embedRenderMessageVariant ''UniWorX ''SheetTypeHeader ("SheetType" <>)
|
embedRenderMessageVariant ''UniWorX ''SheetTypeHeader ("SheetType" <>)
|
||||||
|
|
||||||
@ -3347,6 +3351,7 @@ data CampusUserConversionException
|
|||||||
| CampusUserInvalidSurname
|
| CampusUserInvalidSurname
|
||||||
| CampusUserInvalidTitle
|
| CampusUserInvalidTitle
|
||||||
| CampusUserInvalidMatriculation
|
| CampusUserInvalidMatriculation
|
||||||
|
| CampusUserInvalidSex
|
||||||
| CampusUserInvalidFeaturesOfStudy Text
|
| CampusUserInvalidFeaturesOfStudy Text
|
||||||
| CampusUserInvalidAssociatedSchools Text
|
| CampusUserInvalidAssociatedSchools Text
|
||||||
deriving (Eq, Ord, Read, Show, Generic, Typeable)
|
deriving (Eq, Ord, Read, Show, Generic, Typeable)
|
||||||
@ -3366,6 +3371,7 @@ upsertCampusUser ldapData Creds{..} = do
|
|||||||
userFirstName' = fold [ v | (k, v) <- ldapData, k == ldapUserFirstName ]
|
userFirstName' = fold [ v | (k, v) <- ldapData, k == ldapUserFirstName ]
|
||||||
userSurname' = fold [ v | (k, v) <- ldapData, k == ldapUserSurname ]
|
userSurname' = fold [ v | (k, v) <- ldapData, k == ldapUserSurname ]
|
||||||
userTitle' = fold [ v | (k, v) <- ldapData, k == ldapUserTitle ]
|
userTitle' = fold [ v | (k, v) <- ldapData, k == ldapUserTitle ]
|
||||||
|
userSex' = fold [ v | (k, v) <- ldapData, k == ldapSex ]
|
||||||
|
|
||||||
userAuthentication
|
userAuthentication
|
||||||
| isPWHash = error "PWHash should only work for users that are already known"
|
| isPWHash = error "PWHash should only work for users that are already known"
|
||||||
@ -3412,6 +3418,16 @@ upsertCampusUser ldapData Creds{..} = do
|
|||||||
-> return Nothing
|
-> return Nothing
|
||||||
| otherwise
|
| otherwise
|
||||||
-> throwM CampusUserInvalidMatriculation
|
-> throwM CampusUserInvalidMatriculation
|
||||||
|
userSex <- if
|
||||||
|
| [bs] <- userSex'
|
||||||
|
, Right userSex'' <- Text.decodeUtf8' bs
|
||||||
|
, Just userSex''' <- readMay userSex''
|
||||||
|
, Just userSex <- userSex''' ^? iso5218
|
||||||
|
-> return $ Just userSex
|
||||||
|
| [] <- userSex'
|
||||||
|
-> return Nothing
|
||||||
|
| otherwise
|
||||||
|
-> throwM CampusUserInvalidSex
|
||||||
|
|
||||||
let
|
let
|
||||||
newUser = User
|
newUser = User
|
||||||
@ -3424,6 +3440,7 @@ upsertCampusUser ldapData Creds{..} = do
|
|||||||
, userTimeFormat = userDefaultTimeFormat
|
, userTimeFormat = userDefaultTimeFormat
|
||||||
, userDownloadFiles = userDefaultDownloadFiles
|
, userDownloadFiles = userDefaultDownloadFiles
|
||||||
, userWarningDays = userDefaultWarningDays
|
, userWarningDays = userDefaultWarningDays
|
||||||
|
, userShowSex = userDefaultShowSex
|
||||||
, userNotificationSettings = def
|
, userNotificationSettings = def
|
||||||
, userMailLanguages = def
|
, userMailLanguages = def
|
||||||
, userCsvOptions = def
|
, userCsvOptions = def
|
||||||
|
|||||||
@ -32,7 +32,7 @@ postCUserR tid ssh csh uCId = do
|
|||||||
-- - User is a tutor for course
|
-- - User is a tutor for course
|
||||||
-- - User is a lecturer for course
|
-- - User is a lecturer for course
|
||||||
let currentRoute = CourseR tid ssh csh (CUserR uCId)
|
let currentRoute = CourseR tid ssh csh (CUserR uCId)
|
||||||
dozentId <- requireAuthId
|
Entity dozentId (userShowSex -> showSex) <- requireAuth
|
||||||
uid <- decrypt uCId
|
uid <- decrypt uCId
|
||||||
-- DB reads
|
-- DB reads
|
||||||
(cid, User{..}, mRegistration, thisUniqueNote, noteText, noteEdits, studies) <- runDB $ do
|
(cid, User{..}, mRegistration, thisUniqueNote, noteText, noteEdits, studies) <- runDB $ do
|
||||||
@ -180,8 +180,15 @@ postCUserR tid ssh csh uCId = do
|
|||||||
|
|
||||||
-- generate output
|
-- generate output
|
||||||
let headingLong
|
let headingLong
|
||||||
| is _Just mRegistration = [whamlet|^{nameWidget userDisplayName userSurname}, _{MsgCourseMemberOf} #{csh} #{tid}|]
|
| is _Just mRegistration
|
||||||
| otherwise = [whamlet|^{nameWidget userDisplayName userSurname}, _{MsgCourseAssociatedWith} #{csh} #{tid}|]
|
, Just sex <- guardOn showSex =<< userSex
|
||||||
|
= [whamlet|^{nameWidget userDisplayName userSurname} (_{ShortSex sex}), _{MsgCourseMemberOf} #{csh} #{tid}|]
|
||||||
|
| is _Just mRegistration
|
||||||
|
= [whamlet|^{nameWidget userDisplayName userSurname}, _{MsgCourseMemberOf} #{csh} #{tid}|]
|
||||||
|
| Just sex <- guardOn showSex =<< userSex
|
||||||
|
= [whamlet|^{nameWidget userDisplayName userSurname} (_{ShortSex sex}), _{MsgCourseAssociatedWith} #{csh} #{tid}|]
|
||||||
|
| otherwise
|
||||||
|
= [whamlet|^{nameWidget userDisplayName userSurname}, _{MsgCourseAssociatedWith} #{csh} #{tid}|]
|
||||||
headingShort = prependCourseTitle tid ssh csh $ SomeMessage userDisplayName
|
headingShort = prependCourseTitle tid ssh csh $ SomeMessage userDisplayName
|
||||||
siteLayout headingLong $ do
|
siteLayout headingLong $ do
|
||||||
setTitleI headingShort
|
setTitleI headingShort
|
||||||
|
|||||||
@ -4,7 +4,7 @@ module Handler.Course.Users
|
|||||||
( queryUser
|
( queryUser
|
||||||
, makeCourseUserTable
|
, makeCourseUserTable
|
||||||
, postCUsersR, getCUsersR
|
, postCUsersR, getCUsersR
|
||||||
, colUserDegreeShort, colUserField, colUserSemester
|
, colUserDegreeShort, colUserField, colUserSemester, colUserSex'
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Import
|
import Import
|
||||||
@ -144,6 +144,9 @@ colUserDegreeShort :: IsDBTable m c => Colonnade Sortable UserTableData (DBCell
|
|||||||
colUserDegreeShort = sortable (Just "degree-short") (i18nCell MsgStudyFeatureDegree) $
|
colUserDegreeShort = sortable (Just "degree-short") (i18nCell MsgStudyFeatureDegree) $
|
||||||
foldMap (i18nCell . ShortStudyDegree) . preview (_userTableFeatures . _2 . _Just)
|
foldMap (i18nCell . ShortStudyDegree) . preview (_userTableFeatures . _2 . _Just)
|
||||||
|
|
||||||
|
colUserSex' :: IsDBTable m c => Colonnade Sortable UserTableData (DBCell m c)
|
||||||
|
colUserSex' = colUserSex $ hasUser . _userSex
|
||||||
|
|
||||||
|
|
||||||
data UserTableCsvStudyFeature = UserTableCsvStudyFeature
|
data UserTableCsvStudyFeature = UserTableCsvStudyFeature
|
||||||
{ csvUserField :: Text
|
{ csvUserField :: Text
|
||||||
@ -155,6 +158,7 @@ makeLenses_ ''UserTableCsvStudyFeature
|
|||||||
|
|
||||||
data UserTableCsv = UserTableCsv
|
data UserTableCsv = UserTableCsv
|
||||||
{ csvUserName :: Text
|
{ csvUserName :: Text
|
||||||
|
, csvUserSex :: Maybe Sex
|
||||||
, csvUserMatriculation :: Maybe Text
|
, csvUserMatriculation :: Maybe Text
|
||||||
, csvUserEmail :: CI Email
|
, csvUserEmail :: CI Email
|
||||||
, csvUserStudyFeatures :: Either (Maybe UserTableCsvStudyFeature) (Set UserTableCsvStudyFeature)
|
, csvUserStudyFeatures :: Either (Maybe UserTableCsvStudyFeature) (Set UserTableCsvStudyFeature)
|
||||||
@ -167,6 +171,7 @@ makeLenses_ ''UserTableCsv
|
|||||||
instance Csv.ToNamedRecord UserTableCsv where
|
instance Csv.ToNamedRecord UserTableCsv where
|
||||||
toNamedRecord UserTableCsv{..} = Csv.namedRecord $
|
toNamedRecord UserTableCsv{..} = Csv.namedRecord $
|
||||||
[ "name" Csv..= csvUserName
|
[ "name" Csv..= csvUserName
|
||||||
|
, "sex" Csv..= csvUserSex
|
||||||
, "matriculation" Csv..= csvUserMatriculation
|
, "matriculation" Csv..= csvUserMatriculation
|
||||||
, "email" Csv..= csvUserEmail
|
, "email" Csv..= csvUserEmail
|
||||||
] ++ case csvUserStudyFeatures of
|
] ++ case csvUserStudyFeatures of
|
||||||
@ -194,6 +199,7 @@ instance Csv.ToNamedRecord UserTableCsv where
|
|||||||
instance CsvColumnsExplained UserTableCsv where
|
instance CsvColumnsExplained UserTableCsv where
|
||||||
csvColumnsExplanations _ = mconcat
|
csvColumnsExplanations _ = mconcat
|
||||||
[ single "name" MsgCsvColumnUserName
|
[ single "name" MsgCsvColumnUserName
|
||||||
|
, single "sex" MsgCsvColumnUserSex
|
||||||
, single "matriculation" MsgCsvColumnUserMatriculation
|
, single "matriculation" MsgCsvColumnUserMatriculation
|
||||||
, single "email" MsgCsvColumnUserEmail
|
, single "email" MsgCsvColumnUserEmail
|
||||||
, single "study-features" MsgCsvColumnUserStudyFeatures
|
, single "study-features" MsgCsvColumnUserStudyFeatures
|
||||||
@ -214,9 +220,11 @@ newtype UserCsvExportData = UserCsvExportData
|
|||||||
instance Default UserCsvExportData where
|
instance Default UserCsvExportData where
|
||||||
def = UserCsvExportData True
|
def = UserCsvExportData True
|
||||||
|
|
||||||
userTableCsvHeader :: UserCsvExportData -> [Entity Tutorial] -> Csv.Header
|
userTableCsvHeader :: Bool -> UserCsvExportData -> [Entity Tutorial] -> Csv.Header
|
||||||
userTableCsvHeader UserCsvExportData{..} tuts = Csv.header $
|
userTableCsvHeader showSex UserCsvExportData{..} tuts = Csv.header $
|
||||||
[ "name", "matriculation", "email"
|
[ "name" ] ++
|
||||||
|
[ "sex" | showSex ] ++
|
||||||
|
[ "matriculation", "email"
|
||||||
] ++ bool (pure "study-features") ["field", "degree", "semester"] csvUserSimplifiedFeaturesOfStudy ++
|
] ++ bool (pure "study-features") ["field", "degree", "semester"] csvUserSimplifiedFeaturesOfStudy ++
|
||||||
[ "tutorial" | hasEmptyRegGroup ] ++
|
[ "tutorial" | hasEmptyRegGroup ] ++
|
||||||
map (encodeUtf8 . CI.foldedCase) regGroups ++
|
map (encodeUtf8 . CI.foldedCase) regGroups ++
|
||||||
@ -264,6 +272,7 @@ makeCourseUserTable cid acts restrict colChoices psValidator csvColumns = do
|
|||||||
csvName <- getMessageRender <*> pure (MsgCourseUserCsvName courseTerm courseSchool courseShorthand)
|
csvName <- getMessageRender <*> pure (MsgCourseUserCsvName courseTerm courseSchool courseShorthand)
|
||||||
tutorials <- selectList [ TutorialCourse ==. cid ] []
|
tutorials <- selectList [ TutorialCourse ==. cid ] []
|
||||||
-- -- psValidator has default sorting and filtering
|
-- -- psValidator has default sorting and filtering
|
||||||
|
showSex <- getShowSex
|
||||||
let dbtIdent = "courseUsers" :: Text
|
let dbtIdent = "courseUsers" :: Text
|
||||||
dbtStyle = def { dbsFilterLayout = defaultDBSFilterLayout }
|
dbtStyle = def { dbsFilterLayout = defaultDBSFilterLayout }
|
||||||
dbtSQLQuery q = userTableQuery cid q <* E.where_ (restrict q)
|
dbtSQLQuery q = userTableQuery cid q <* E.where_ (restrict q)
|
||||||
@ -276,24 +285,25 @@ makeCourseUserTable cid acts restrict colChoices psValidator csvColumns = do
|
|||||||
tuts = foldr (\tut@(Entity _ Tutorial{..}) -> maybe (over _1 $ cons tut) (over _2 . flip (Map.insertWith (<|>)) (Just tut)) tutorialRegGroup) ([], Map.fromSet (const Nothing) regGroups) tuts'
|
tuts = foldr (\tut@(Entity _ Tutorial{..}) -> maybe (over _1 $ cons tut) (over _2 . flip (Map.insertWith (<|>)) (Just tut)) tutorialRegGroup) ([], Map.fromSet (const Nothing) regGroups) tuts'
|
||||||
return (user, registrationTime, userNoteId, (entityVal <$> feature, entityVal <$> degree, entityVal <$> terms), tuts)
|
return (user, registrationTime, userNoteId, (entityVal <$> feature, entityVal <$> degree, entityVal <$> terms), tuts)
|
||||||
dbtColonnade = colChoices
|
dbtColonnade = colChoices
|
||||||
dbtSorting = Map.fromList
|
dbtSorting = mconcat
|
||||||
[ sortUserNameLink queryUser -- slower sorting through clicking name column header
|
[ single $ sortUserNameLink queryUser -- slower sorting through clicking name column header
|
||||||
, sortUserSurname queryUser -- needed for initial sorting
|
, single $ sortUserSurname queryUser -- needed for initial sorting
|
||||||
, sortUserDisplayName queryUser -- needed for initial sorting
|
, single $ sortUserDisplayName queryUser -- needed for initial sorting
|
||||||
, sortUserEmail queryUser
|
, single $ sortUserEmail queryUser
|
||||||
, sortUserMatriclenr queryUser
|
, single $ sortUserMatriclenr queryUser
|
||||||
, ("degree" , SortColumn $ queryFeaturesDegree >>> (E.?. StudyDegreeName))
|
, sortUserSex (to queryUser . to (E.^. UserSex))
|
||||||
, ("degree-short", SortColumn $ queryFeaturesDegree >>> (E.?. StudyDegreeShorthand))
|
, single $ ("degree" , SortColumn $ queryFeaturesDegree >>> (E.?. StudyDegreeName))
|
||||||
, ("field" , SortColumn $ queryFeaturesField >>> (E.?. StudyTermsName))
|
, single $ ("degree-short", SortColumn $ queryFeaturesDegree >>> (E.?. StudyDegreeShorthand))
|
||||||
, ("field-short" , SortColumn $ queryFeaturesField >>> (E.?. StudyTermsShorthand))
|
, single $ ("field" , SortColumn $ queryFeaturesField >>> (E.?. StudyTermsName))
|
||||||
, ("semesternr" , SortColumn $ queryFeaturesStudy >>> (E.?. StudyFeaturesSemester))
|
, single $ ("field-short" , SortColumn $ queryFeaturesField >>> (E.?. StudyTermsShorthand))
|
||||||
, ("registration", SortColumn $ queryParticipant >>> (E.^. CourseParticipantRegistration))
|
, single $ ("semesternr" , SortColumn $ queryFeaturesStudy >>> (E.?. StudyFeaturesSemester))
|
||||||
, ("note" , SortColumn $ queryUserNote >>> \note -> -- sort by last edit date
|
, single $ ("registration", SortColumn $ queryParticipant >>> (E.^. CourseParticipantRegistration))
|
||||||
|
, single $ ("note" , SortColumn $ queryUserNote >>> \note -> -- sort by last edit date
|
||||||
E.sub_select . E.from $ \edit -> do
|
E.sub_select . E.from $ \edit -> do
|
||||||
E.where_ $ note E.?. CourseUserNoteId E.==. E.just (edit E.^. CourseUserNoteEditNote)
|
E.where_ $ note E.?. CourseUserNoteId E.==. E.just (edit E.^. CourseUserNoteEditNote)
|
||||||
return . E.max_ $ edit E.^. CourseUserNoteEditTime
|
return . E.max_ $ edit E.^. CourseUserNoteEditTime
|
||||||
)
|
)
|
||||||
, ("tutorials" , SortColumn $ queryUser >>> \user ->
|
, single $ ("tutorials" , SortColumn $ queryUser >>> \user ->
|
||||||
E.sub_select . E.from $ \(tutorial `E.InnerJoin` participant) -> do
|
E.sub_select . E.from $ \(tutorial `E.InnerJoin` participant) -> do
|
||||||
E.on $ tutorial E.^. TutorialId E.==. participant E.^. TutorialParticipantTutorial
|
E.on $ tutorial E.^. TutorialId E.==. participant E.^. TutorialParticipantTutorial
|
||||||
E.&&. tutorial E.^. TutorialCourse E.==. E.val cid
|
E.&&. tutorial E.^. TutorialCourse E.==. E.val cid
|
||||||
@ -301,39 +311,44 @@ makeCourseUserTable cid acts restrict colChoices psValidator csvColumns = do
|
|||||||
return . E.min_ $ tutorial E.^. TutorialName
|
return . E.min_ $ tutorial E.^. TutorialName
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
dbtFilter = Map.fromList
|
where single = uncurry Map.singleton
|
||||||
[ fltrUserNameLink queryUser
|
dbtFilter = mconcat
|
||||||
, fltrUserEmail queryUser
|
[ single $ fltrUserNameLink queryUser
|
||||||
, fltrUserMatriclenr queryUser
|
, single $ fltrUserEmail queryUser
|
||||||
, fltrUserNameEmail queryUser
|
, single $ fltrUserMatriclenr queryUser
|
||||||
, ("field-name" , FilterColumn $ E.mkContainsFilter $ queryFeaturesField >>> (E.?. StudyTermsName))
|
, single $ fltrUserNameEmail queryUser
|
||||||
, ("field-short" , FilterColumn $ E.mkContainsFilter $ queryFeaturesField >>> (E.?. StudyTermsShorthand))
|
, fltrUserSex (to queryUser . to (E.^. UserSex))
|
||||||
, ("field-key" , FilterColumn $ E.mkExactFilter $ queryFeaturesField >>> (E.?. StudyTermsKey))
|
, single $ ("field-name" , FilterColumn $ E.mkContainsFilter $ queryFeaturesField >>> (E.?. StudyTermsName))
|
||||||
, ("field" , FilterColumn $ E.anyFilter
|
, single $ ("field-short" , FilterColumn $ E.mkContainsFilter $ queryFeaturesField >>> (E.?. StudyTermsShorthand))
|
||||||
[ E.mkContainsFilterWith Just $ queryFeaturesField >>> E.joinV . (E.?. StudyTermsName)
|
, single $ ("field-key" , FilterColumn $ E.mkExactFilter $ queryFeaturesField >>> (E.?. StudyTermsKey))
|
||||||
, E.mkContainsFilterWith Just $ queryFeaturesField >>> E.joinV . (E.?. StudyTermsShorthand)
|
, single $ ("field" , FilterColumn $ E.anyFilter
|
||||||
, E.mkExactFilterWith readMay $ queryFeaturesField >>> (E.?. StudyTermsKey)
|
[ E.mkContainsFilterWith Just $ queryFeaturesField >>> E.joinV . (E.?. StudyTermsName)
|
||||||
] )
|
, E.mkContainsFilterWith Just $ queryFeaturesField >>> E.joinV . (E.?. StudyTermsShorthand)
|
||||||
, ("degree" , FilterColumn $ E.anyFilter
|
, E.mkExactFilterWith readMay $ queryFeaturesField >>> (E.?. StudyTermsKey)
|
||||||
[ E.mkContainsFilterWith Just $ queryFeaturesDegree >>> E.joinV . (E.?. StudyDegreeName)
|
] )
|
||||||
, E.mkContainsFilterWith Just $ queryFeaturesDegree >>> E.joinV . (E.?. StudyDegreeShorthand)
|
, single $ ("degree" , FilterColumn $ E.anyFilter
|
||||||
, E.mkExactFilterWith readMay $ queryFeaturesDegree >>> (E.?. StudyDegreeKey)
|
[ E.mkContainsFilterWith Just $ queryFeaturesDegree >>> E.joinV . (E.?. StudyDegreeName)
|
||||||
] )
|
, E.mkContainsFilterWith Just $ queryFeaturesDegree >>> E.joinV . (E.?. StudyDegreeShorthand)
|
||||||
, ("semesternr" , FilterColumn $ E.mkExactFilter $ queryFeaturesStudy >>> (E.?. StudyFeaturesSemester))
|
, E.mkExactFilterWith readMay $ queryFeaturesDegree >>> (E.?. StudyDegreeKey)
|
||||||
, ("tutorial" , FilterColumn $ E.mkExistsFilter $ \row criterion ->
|
] )
|
||||||
E.from $ \(tutorial `E.InnerJoin` tutorialParticipant) -> do
|
, single $ ("semesternr" , FilterColumn $ E.mkExactFilter $ queryFeaturesStudy >>> (E.?. StudyFeaturesSemester))
|
||||||
E.on $ tutorial E.^. TutorialId E.==. tutorialParticipant E.^. TutorialParticipantTutorial
|
, single $ ("tutorial" , FilterColumn $ E.mkExistsFilter $ \row criterion ->
|
||||||
E.where_ $ tutorial E.^. TutorialCourse E.==. E.val cid
|
E.from $ \(tutorial `E.InnerJoin` tutorialParticipant) -> do
|
||||||
E.&&. E.hasInfix (tutorial E.^. TutorialName) (E.val criterion :: E.SqlExpr (E.Value (CI Text)))
|
E.on $ tutorial E.^. TutorialId E.==. tutorialParticipant E.^. TutorialParticipantTutorial
|
||||||
E.&&. tutorialParticipant E.^. TutorialParticipantUser E.==. queryUser row E.^. UserId
|
E.where_ $ tutorial E.^. TutorialCourse E.==. E.val cid
|
||||||
)
|
E.&&. E.hasInfix (tutorial E.^. TutorialName) (E.val criterion :: E.SqlExpr (E.Value (CI Text)))
|
||||||
|
E.&&. tutorialParticipant E.^. TutorialParticipantUser E.==. queryUser row E.^. UserId
|
||||||
|
)
|
||||||
-- , ("course-registration", error "TODO") -- TODO
|
-- , ("course-registration", error "TODO") -- TODO
|
||||||
-- , ("course-user-note", error "TODO") -- TODO
|
-- , ("course-user-note", error "TODO") -- TODO
|
||||||
]
|
]
|
||||||
dbtFilterUI mPrev = mconcat
|
where single = uncurry Map.singleton
|
||||||
|
dbtFilterUI mPrev = mconcat $
|
||||||
[ fltrUserNameEmailUI mPrev
|
[ fltrUserNameEmailUI mPrev
|
||||||
, fltrUserMatriclenrUI mPrev
|
, fltrUserMatriclenrUI mPrev
|
||||||
, prismAForm (singletonFilter "degree") mPrev $ aopt textField (fslI MsgStudyFeatureDegree)
|
] ++
|
||||||
|
[ fltrUserSexUI mPrev | showSex ] ++
|
||||||
|
[ prismAForm (singletonFilter "degree") mPrev $ aopt textField (fslI MsgStudyFeatureDegree)
|
||||||
, prismAForm (singletonFilter "field") mPrev $ aopt textField (fslI MsgCourseStudyFeature)
|
, prismAForm (singletonFilter "field") mPrev $ aopt textField (fslI MsgCourseStudyFeature)
|
||||||
, prismAForm (singletonFilter "tutorial") mPrev $ aopt textField (fslI MsgCourseTutorial)
|
, prismAForm (singletonFilter "tutorial") mPrev $ aopt textField (fslI MsgCourseTutorial)
|
||||||
]
|
]
|
||||||
@ -358,6 +373,7 @@ makeCourseUserTable cid acts restrict colChoices psValidator csvColumns = do
|
|||||||
, dbtCsvDoEncode = \UserCsvExportData{..} -> C.mapM $ \(E.Value uid, row) -> flip runReaderT row $
|
, dbtCsvDoEncode = \UserCsvExportData{..} -> C.mapM $ \(E.Value uid, row) -> flip runReaderT row $
|
||||||
UserTableCsv
|
UserTableCsv
|
||||||
<$> view (hasUser . _userDisplayName)
|
<$> view (hasUser . _userDisplayName)
|
||||||
|
<*> view (hasUser . _userSex)
|
||||||
<*> view (hasUser . _userMatrikelnummer)
|
<*> view (hasUser . _userMatrikelnummer)
|
||||||
<*> view (hasUser . _userEmail)
|
<*> view (hasUser . _userEmail)
|
||||||
<*> if
|
<*> if
|
||||||
@ -395,7 +411,7 @@ makeCourseUserTable cid acts restrict colChoices psValidator csvColumns = do
|
|||||||
<*> (over (_2.traverse._Just) (tutorialName . entityVal) . over (_1.traverse) (tutorialName . entityVal) <$> view _userTutorials)
|
<*> (over (_2.traverse._Just) (tutorialName . entityVal) . over (_1.traverse) (tutorialName . entityVal) <$> view _userTutorials)
|
||||||
, dbtCsvName = unpack csvName
|
, dbtCsvName = unpack csvName
|
||||||
, dbtCsvNoExportData = Nothing
|
, dbtCsvNoExportData = Nothing
|
||||||
, dbtCsvHeader = return . Vector.filter csvColumns' . flip userTableCsvHeader tutorials . fromMaybe def
|
, dbtCsvHeader = return . Vector.filter csvColumns' . flip (userTableCsvHeader showSex) tutorials . fromMaybe def
|
||||||
}
|
}
|
||||||
where
|
where
|
||||||
userNote = runMaybeT $ do
|
userNote = runMaybeT $ do
|
||||||
@ -424,6 +440,7 @@ courseUserDeregisterForm cid = wFormToAForm $ do
|
|||||||
getCUsersR, postCUsersR :: TermId -> SchoolId -> CourseShorthand -> Handler Html
|
getCUsersR, postCUsersR :: TermId -> SchoolId -> CourseShorthand -> Handler Html
|
||||||
getCUsersR = postCUsersR
|
getCUsersR = postCUsersR
|
||||||
postCUsersR tid ssh csh = do
|
postCUsersR tid ssh csh = do
|
||||||
|
showSex <- getShowSex
|
||||||
(Entity cid course, numParticipants, (participantRes,participantTable)) <- runDB $ do
|
(Entity cid course, numParticipants, (participantRes,participantTable)) <- runDB $ do
|
||||||
mayRegister <- hasWriteAccessTo $ CourseR tid ssh csh CAddUserR
|
mayRegister <- hasWriteAccessTo $ CourseR tid ssh csh CAddUserR
|
||||||
ent@(Entity cid _) <- getBy404 $ TermSchoolCourseShort tid ssh csh
|
ent@(Entity cid _) <- getBy404 $ TermSchoolCourseShort tid ssh csh
|
||||||
@ -431,6 +448,7 @@ postCUsersR tid ssh csh = do
|
|||||||
let colChoices = mconcat $ catMaybes
|
let colChoices = mconcat $ catMaybes
|
||||||
[ pure $ dbSelect (applying _2) id (return . view (hasEntity . _entityKey))
|
[ pure $ dbSelect (applying _2) id (return . view (hasEntity . _entityKey))
|
||||||
, pure $ colUserNameLink (CourseR tid ssh csh . CUserR)
|
, pure $ colUserNameLink (CourseR tid ssh csh . CUserR)
|
||||||
|
, guardOn showSex $ colUserSex'
|
||||||
, pure $ colUserEmail
|
, pure $ colUserEmail
|
||||||
, pure $ colUserMatriclenr
|
, pure $ colUserMatriclenr
|
||||||
, pure $ colUserDegreeShort
|
, pure $ colUserDegreeShort
|
||||||
|
|||||||
@ -39,6 +39,7 @@ data SettingsForm = SettingsForm
|
|||||||
, stgTime :: DateTimeFormat
|
, stgTime :: DateTimeFormat
|
||||||
, stgDownloadFiles :: Bool
|
, stgDownloadFiles :: Bool
|
||||||
, stgWarningDays :: NominalDiffTime
|
, stgWarningDays :: NominalDiffTime
|
||||||
|
, stgShowSex :: Bool
|
||||||
, stgSchools :: Set SchoolId
|
, stgSchools :: Set SchoolId
|
||||||
, stgNotificationSettings :: NotificationSettings
|
, stgNotificationSettings :: NotificationSettings
|
||||||
}
|
}
|
||||||
@ -96,6 +97,7 @@ makeSettingForm template html = do
|
|||||||
<*> areq daysField (fslI MsgWarningDays
|
<*> areq daysField (fslI MsgWarningDays
|
||||||
& setTooltip MsgWarningDaysTip
|
& setTooltip MsgWarningDaysTip
|
||||||
) (stgWarningDays <$> template)
|
) (stgWarningDays <$> template)
|
||||||
|
<*> apopt checkBoxField (fslI MsgShowSex & setTooltip MsgShowSexTip) (stgShowSex <$> template)
|
||||||
<* aformSection MsgFormNotifications
|
<* aformSection MsgFormNotifications
|
||||||
<*> schoolsForm (stgSchools <$> template)
|
<*> schoolsForm (stgSchools <$> template)
|
||||||
<*> notificationForm (stgNotificationSettings <$> template)
|
<*> notificationForm (stgNotificationSettings <$> template)
|
||||||
@ -257,6 +259,7 @@ postProfileR = do
|
|||||||
, stgSchools = userSchools
|
, stgSchools = userSchools
|
||||||
, stgNotificationSettings = userNotificationSettings
|
, stgNotificationSettings = userNotificationSettings
|
||||||
, stgWarningDays = userWarningDays
|
, stgWarningDays = userWarningDays
|
||||||
|
, stgShowSex = userShowSex
|
||||||
}
|
}
|
||||||
((res,formWidget), formEnctype) <- runFormPost . validateForm (validateSettings user) . identifyForm ProfileSettings $ makeSettingForm settingsTemplate
|
((res,formWidget), formEnctype) <- runFormPost . validateForm (validateSettings user) . identifyForm ProfileSettings $ makeSettingForm settingsTemplate
|
||||||
|
|
||||||
@ -273,6 +276,7 @@ postProfileR = do
|
|||||||
, UserDownloadFiles =. stgDownloadFiles
|
, UserDownloadFiles =. stgDownloadFiles
|
||||||
, UserWarningDays =. stgWarningDays
|
, UserWarningDays =. stgWarningDays
|
||||||
, UserNotificationSettings =. stgNotificationSettings
|
, UserNotificationSettings =. stgNotificationSettings
|
||||||
|
, UserShowSex =. stgShowSex
|
||||||
] ++ [ UserDisplayEmail =. stgDisplayEmail | userDisplayEmail == stgDisplayEmail ]
|
] ++ [ UserDisplayEmail =. stgDisplayEmail | userDisplayEmail == stgDisplayEmail ]
|
||||||
when (stgDisplayEmail /= userDisplayEmail) $ do
|
when (stgDisplayEmail /= userDisplayEmail) $ do
|
||||||
queueDBJob $ JobChangeUserDisplayEmail uid stgDisplayEmail
|
queueDBJob $ JobChangeUserDisplayEmail uid stgDisplayEmail
|
||||||
|
|||||||
@ -33,16 +33,18 @@ embedRenderMessage ''UniWorX ''TutorialUserAction id
|
|||||||
getTUsersR, postTUsersR :: TermId -> SchoolId -> CourseShorthand -> TutorialName -> Handler Html
|
getTUsersR, postTUsersR :: TermId -> SchoolId -> CourseShorthand -> TutorialName -> Handler Html
|
||||||
getTUsersR = postTUsersR
|
getTUsersR = postTUsersR
|
||||||
postTUsersR tid ssh csh tutn = do
|
postTUsersR tid ssh csh tutn = do
|
||||||
|
showSex <- getShowSex
|
||||||
(Entity tutid Tutorial{..}, (participantRes, participantTable)) <- runDB $ do
|
(Entity tutid Tutorial{..}, (participantRes, participantTable)) <- runDB $ do
|
||||||
tut@(Entity tutid _) <- fetchTutorial tid ssh csh tutn
|
tut@(Entity tutid _) <- fetchTutorial tid ssh csh tutn
|
||||||
let colChoices = mconcat
|
let colChoices = mconcat $ catMaybes
|
||||||
[ dbSelect (applying _2) id (return . view (hasEntity . _entityKey))
|
[ pure $ dbSelect (applying _2) id (return . view (hasEntity . _entityKey))
|
||||||
, colUserName
|
, pure colUserName
|
||||||
, colUserEmail
|
, guardOn showSex colUserSex'
|
||||||
, colUserMatriclenr
|
, pure colUserEmail
|
||||||
, colUserDegreeShort
|
, pure colUserMatriclenr
|
||||||
, colUserField
|
, pure colUserDegreeShort
|
||||||
, colUserSemester
|
, pure colUserField
|
||||||
|
, pure colUserSemester
|
||||||
]
|
]
|
||||||
psValidator = def
|
psValidator = def
|
||||||
& defaultSortingByName
|
& defaultSortingByName
|
||||||
|
|||||||
@ -34,6 +34,8 @@ import Data.Aeson hiding (Result(..))
|
|||||||
|
|
||||||
import Handler.Users.Add as Handler.Users
|
import Handler.Users.Add as Handler.Users
|
||||||
|
|
||||||
|
import qualified Data.Conduit.List as C
|
||||||
|
|
||||||
|
|
||||||
hijackUserForm :: Form ()
|
hijackUserForm :: Form ()
|
||||||
hijackUserForm csrf = do
|
hijackUserForm csrf = do
|
||||||
@ -54,6 +56,18 @@ instance Finite UserAction
|
|||||||
nullaryPathPiece ''UserAction $ camelToPathPiece' 1
|
nullaryPathPiece ''UserAction $ camelToPathPiece' 1
|
||||||
embedRenderMessage ''UniWorX ''UserAction id
|
embedRenderMessage ''UniWorX ''UserAction id
|
||||||
|
|
||||||
|
|
||||||
|
data AllUsersAction = AllUsersLdapSync
|
||||||
|
deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic, Typeable)
|
||||||
|
|
||||||
|
instance Universe AllUsersAction
|
||||||
|
instance Finite AllUsersAction
|
||||||
|
nullaryPathPiece ''AllUsersAction $ camelToPathPiece' 1
|
||||||
|
embedRenderMessage ''UniWorX ''AllUsersAction id
|
||||||
|
|
||||||
|
instance Button UniWorX AllUsersAction where
|
||||||
|
btnClasses _ = [BCIsButton, BCPrimary]
|
||||||
|
|
||||||
getUsersR, postUsersR :: Handler Html
|
getUsersR, postUsersR :: Handler Html
|
||||||
getUsersR = postUsersR
|
getUsersR = postUsersR
|
||||||
postUsersR = do
|
postUsersR = do
|
||||||
@ -213,6 +227,19 @@ postUsersR = do
|
|||||||
hijackUser uid >>= sendResponse
|
hijackUser uid >>= sendResponse
|
||||||
_other -> error "Should not be possible"
|
_other -> error "Should not be possible"
|
||||||
|
|
||||||
|
((allUsersRes, allUsersWgt), allUsersEnctype) <- runFormPost . identifyForm FIDAllUsersAction $ buttonForm
|
||||||
|
|
||||||
|
formResult allUsersRes $ \case
|
||||||
|
AllUsersLdapSync -> do
|
||||||
|
runDBJobs . runConduit $ selectSource [] [] .| C.mapM_ (queueDBJob . JobSynchroniseLdapUser . entityKey)
|
||||||
|
addMessageI Success $ MsgSynchroniseLdapAllUsersQueued
|
||||||
|
redirect UsersR
|
||||||
|
let allUsersWgt' = wrapForm allUsersWgt def
|
||||||
|
{ formSubmit = FormNoSubmit
|
||||||
|
, formAction = Just $ SomeRoute UsersR
|
||||||
|
, formEncoding = allUsersEnctype
|
||||||
|
}
|
||||||
|
|
||||||
defaultLayout $ do
|
defaultLayout $ do
|
||||||
setTitleI MsgUserListTitle
|
setTitleI MsgUserListTitle
|
||||||
$(widgetFile "users")
|
$(widgetFile "users")
|
||||||
|
|||||||
@ -18,6 +18,7 @@ data AdminUserForm = AdminUserForm
|
|||||||
, aufDisplayName :: UserDisplayName
|
, aufDisplayName :: UserDisplayName
|
||||||
, aufDisplayEmail :: UserEmail
|
, aufDisplayEmail :: UserEmail
|
||||||
, aufMatriculation :: Maybe UserMatriculation
|
, aufMatriculation :: Maybe UserMatriculation
|
||||||
|
, aufSex :: Maybe Sex
|
||||||
, aufEmail :: UserEmail
|
, aufEmail :: UserEmail
|
||||||
, aufIdent :: UserIdent
|
, aufIdent :: UserIdent
|
||||||
, aufAuth :: AuthenticationKind
|
, aufAuth :: AuthenticationKind
|
||||||
@ -47,6 +48,7 @@ adminUserForm template = renderAForm FormStandard
|
|||||||
<*> areq (textField & cfStrip) (fslI MsgAdminUserDisplayName) (aufDisplayName <$> template)
|
<*> areq (textField & cfStrip) (fslI MsgAdminUserDisplayName) (aufDisplayName <$> template)
|
||||||
<*> areq (emailField & cfCI) (fslI MsgAdminUserDisplayEmail) (aufDisplayEmail <$> template)
|
<*> areq (emailField & cfCI) (fslI MsgAdminUserDisplayEmail) (aufDisplayEmail <$> template)
|
||||||
<*> aopt (textField & cfStrip) (fslI MsgAdminUserMatriculation) (aufMatriculation <$> template)
|
<*> aopt (textField & cfStrip) (fslI MsgAdminUserMatriculation) (aufMatriculation <$> template)
|
||||||
|
<*> aopt (selectField optionsFinite) (fslI MsgAdminUserSex) (aufSex <$> template)
|
||||||
<*> areq (emailField & cfCI) (fslI MsgAdminUserEmail) (aufEmail <$> template)
|
<*> areq (emailField & cfCI) (fslI MsgAdminUserEmail) (aufEmail <$> template)
|
||||||
<*> areq (textField & cfStrip & cfCI) (fslI MsgAdminUserIdent) (aufIdent <$> template)
|
<*> areq (textField & cfStrip & cfCI) (fslI MsgAdminUserIdent) (aufIdent <$> template)
|
||||||
<*> areq (selectField optionsFinite) (fslI MsgAdminUserAuth) (aufAuth <$> template <|> Just AuthKindLDAP)
|
<*> areq (selectField optionsFinite) (fslI MsgAdminUserAuth) (aufAuth <$> template <|> Just AuthKindLDAP)
|
||||||
@ -72,6 +74,7 @@ postAdminUserAddR = do
|
|||||||
, userTimeFormat = userDefaultTimeFormat
|
, userTimeFormat = userDefaultTimeFormat
|
||||||
, userDownloadFiles = userDefaultDownloadFiles
|
, userDownloadFiles = userDefaultDownloadFiles
|
||||||
, userWarningDays = userDefaultWarningDays
|
, userWarningDays = userDefaultWarningDays
|
||||||
|
, userShowSex = userDefaultShowSex
|
||||||
, userNotificationSettings = def
|
, userNotificationSettings = def
|
||||||
, userMailLanguages = def
|
, userMailLanguages = def
|
||||||
, userCsvOptions = def
|
, userCsvOptions = def
|
||||||
@ -85,6 +88,7 @@ postAdminUserAddR = do
|
|||||||
, userFirstName = aufFirstName
|
, userFirstName = aufFirstName
|
||||||
, userSurname = aufSurname
|
, userSurname = aufSurname
|
||||||
, userTitle = aufTitle
|
, userTitle = aufTitle
|
||||||
|
, userSex = aufSex
|
||||||
, userMatrikelnummer = aufMatriculation
|
, userMatrikelnummer = aufMatriculation
|
||||||
, userAuthentication = mkAuthMode aufAuth
|
, userAuthentication = mkAuthMode aufAuth
|
||||||
}
|
}
|
||||||
|
|||||||
@ -145,3 +145,6 @@ studyFeaturesWidget featId = do
|
|||||||
_{StudyDegreeTerm degree terms}, _{MsgStudyFeatureAge} #{studyFeaturesSemester}
|
_{StudyDegreeTerm degree terms}, _{MsgStudyFeatureAge} #{studyFeaturesSemester}
|
||||||
|]
|
|]
|
||||||
|
|
||||||
|
|
||||||
|
getShowSex :: (MonadHandler m, HandlerSite m ~ UniWorX) => m Bool
|
||||||
|
getShowSex = maybe False (userShowSex . entityVal) <$> maybeAuth
|
||||||
|
|||||||
@ -475,6 +475,22 @@ fltrUserNameUI' :: DBFilterUI
|
|||||||
fltrUserNameUI' mPrev = prismAForm (singletonFilter "user-name") mPrev $ aopt textField (fslI MsgUserDisplayName)
|
fltrUserNameUI' mPrev = prismAForm (singletonFilter "user-name") mPrev $ aopt textField (fslI MsgUserDisplayName)
|
||||||
|
|
||||||
|
|
||||||
|
colUserSex :: OpticColonnade (Maybe Sex)
|
||||||
|
colUserSex resultSex = Colonnade.singleton (fromSortable header) body
|
||||||
|
where
|
||||||
|
header = Sortable (Just "user-sex") (i18nCell MsgSex)
|
||||||
|
body = views resultSex $ maybe mempty i18nCell
|
||||||
|
|
||||||
|
sortUserSex :: OpticSortColumn (Maybe Sex)
|
||||||
|
sortUserSex querySex = singletonMap "user-sex" . SortColumn $ view querySex
|
||||||
|
|
||||||
|
fltrUserSex :: OpticFilterColumn' t (Set Sex) (E.SqlExpr (E.Value (Maybe Sex)))
|
||||||
|
fltrUserSex querySex = singletonMap "user-sex" . FilterColumn $ mkExactFilterWith Just (view querySex)
|
||||||
|
|
||||||
|
fltrUserSexUI :: DBFilterUI
|
||||||
|
fltrUserSexUI mPrev = prismAForm (singletonFilter "user-sex" . maybePrism _PathPiece) mPrev $ aopt (hoistField liftHandler $ selectField optionsFinite :: Field _ Sex) (fslI MsgSex)
|
||||||
|
|
||||||
|
|
||||||
colUserName :: (IsDBTable m c, HasUser a) => Colonnade Sortable a (DBCell m c)
|
colUserName :: (IsDBTable m c, HasUser a) => Colonnade Sortable a (DBCell m c)
|
||||||
colUserName = sortable (Just "user-name") (i18nCell MsgCourseMembers) cellHasUser
|
colUserName = sortable (Just "user-name") (i18nCell MsgCourseMembers) cellHasUser
|
||||||
|
|
||||||
|
|||||||
@ -22,6 +22,8 @@ import qualified Data.Csv as Csv
|
|||||||
|
|
||||||
import qualified Data.Aeson as JSON
|
import qualified Data.Aeson as JSON
|
||||||
|
|
||||||
|
import Database.Persist.Sql (PersistFieldSql(..))
|
||||||
|
|
||||||
|
|
||||||
data StudyFieldType = FieldPrimary | FieldSecondary
|
data StudyFieldType = FieldPrimary | FieldSecondary
|
||||||
deriving (Eq, Ord, Enum, Show, Read, Bounded, Generic)
|
deriving (Eq, Ord, Enum, Show, Read, Bounded, Generic)
|
||||||
@ -145,3 +147,55 @@ deriveJSON defaultOptions
|
|||||||
{ constructorTagModifier = camelToPathPiece' 1
|
{ constructorTagModifier = camelToPathPiece' 1
|
||||||
} ''FavouriteReason
|
} ''FavouriteReason
|
||||||
derivePersistFieldJSON ''FavouriteReason
|
derivePersistFieldJSON ''FavouriteReason
|
||||||
|
|
||||||
|
|
||||||
|
data Sex
|
||||||
|
= SexNotKnown
|
||||||
|
| SexMale
|
||||||
|
| SexFemale
|
||||||
|
| SexNotApplicable
|
||||||
|
deriving (Eq, Ord, Read, Show, Enum, Bounded, Generic, Typeable)
|
||||||
|
instance Universe Sex
|
||||||
|
instance Finite Sex
|
||||||
|
deriveJSON defaultOptions
|
||||||
|
{ constructorTagModifier = camelToPathPiece' 1
|
||||||
|
} ''Sex
|
||||||
|
nullaryPathPiece ''Sex $ camelToPathPiece' 1
|
||||||
|
|
||||||
|
iso5218 :: Integral n => Prism' n Sex
|
||||||
|
iso5218 = prism' sexToWord sexFromWord
|
||||||
|
where
|
||||||
|
sexToWord = \case
|
||||||
|
SexNotKnown -> 0
|
||||||
|
SexMale -> 1
|
||||||
|
SexFemale -> 2
|
||||||
|
SexNotApplicable -> 9
|
||||||
|
sexFromWord = \case
|
||||||
|
0 -> Just SexNotKnown
|
||||||
|
1 -> Just SexMale
|
||||||
|
2 -> Just SexFemale
|
||||||
|
9 -> Just SexNotApplicable
|
||||||
|
_ -> Nothing
|
||||||
|
|
||||||
|
instance PersistField Sex where
|
||||||
|
toPersistValue = PersistInt64 . review iso5218
|
||||||
|
fromPersistValue (PersistInt64 n)
|
||||||
|
| Just s <- n ^? iso5218
|
||||||
|
= Right s
|
||||||
|
fromPersistValue (PersistDouble (toRational -> n))
|
||||||
|
| fromInteger (round n) == n
|
||||||
|
, Just s <- (round n :: Integer) ^? iso5218
|
||||||
|
= Right s
|
||||||
|
fromPersistValue (PersistRational n)
|
||||||
|
| fromInteger (round n) == n
|
||||||
|
, Just s <- (round n :: Integer) ^? iso5218
|
||||||
|
= Right s
|
||||||
|
fromPersistValue x = Left $ "Could not convert “" <> tshow x <> "” to Sex"
|
||||||
|
|
||||||
|
instance PersistFieldSql Sex where
|
||||||
|
sqlType _ = SqlNumeric 1 0
|
||||||
|
|
||||||
|
instance Csv.ToField Sex where
|
||||||
|
toField = Csv.toField . toPathPiece
|
||||||
|
instance Csv.FromField Sex where
|
||||||
|
parseField = maybe (fail "Could not parse Field of type Sex") return . fromPathPiece <=< Csv.parseField
|
||||||
|
|||||||
@ -175,6 +175,7 @@ data UserDefaultConf = UserDefaultConf
|
|||||||
, userDefaultDateTimeFormat, userDefaultDateFormat, userDefaultTimeFormat :: DateTimeFormat
|
, userDefaultDateTimeFormat, userDefaultDateFormat, userDefaultTimeFormat :: DateTimeFormat
|
||||||
, userDefaultDownloadFiles :: Bool
|
, userDefaultDownloadFiles :: Bool
|
||||||
, userDefaultWarningDays :: NominalDiffTime
|
, userDefaultWarningDays :: NominalDiffTime
|
||||||
|
, userDefaultShowSex :: Bool
|
||||||
} deriving (Show)
|
} deriving (Show)
|
||||||
|
|
||||||
data PWHashConf = PWHashConf
|
data PWHashConf = PWHashConf
|
||||||
|
|||||||
@ -227,6 +227,7 @@ data FormIdentifier
|
|||||||
| FIDCommunication
|
| FIDCommunication
|
||||||
| FIDAssignSubmissions
|
| FIDAssignSubmissions
|
||||||
| FIDUserAuthMode
|
| FIDUserAuthMode
|
||||||
|
| FIDAllUsersAction
|
||||||
deriving (Eq, Ord, Read, Show)
|
deriving (Eq, Ord, Read, Show)
|
||||||
|
|
||||||
instance PathPiece FormIdentifier where
|
instance PathPiece FormIdentifier where
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
|
$newline never
|
||||||
<section>
|
<section>
|
||||||
<div .profile>
|
<div .profile>
|
||||||
<dl .deflist.profile-dl>
|
<dl .deflist.profile-dl>
|
||||||
|
$maybe sex <- guardOn showSex =<< userSex
|
||||||
|
<dt .deflist__dt> _{MsgSex}
|
||||||
|
<dd .deflist__dd> _{sex}
|
||||||
<dt .deflist__dt> _{MsgEMail}
|
<dt .deflist__dt> _{MsgEMail}
|
||||||
<dd .deflist__dd> #{mailtoHtml userEmail}
|
<dd .deflist__dd> #{mailtoHtml userEmail}
|
||||||
<dt .deflist__dt> _{MsgMatrikelNr}
|
<dt .deflist__dt> _{MsgMatrikelNr}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ $newline never
|
|||||||
<dd .deflist__dd>
|
<dd .deflist__dd>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Kontrolle über Einstellungen eines Tutoriums kann an Tutoren deligiert werden
|
<li>Kontrolle über Einstellungen eines Tutoriums kann an Tutoren deligiert werden
|
||||||
|
<li>Optionale Anzeige des Geschlechts in Teilnehmerlisten u.Ä.
|
||||||
|
|
||||||
<dt .deflist__dt>
|
<dt .deflist__dt>
|
||||||
^{formatGregorianW 2019 10 10}
|
^{formatGregorianW 2019 10 10}
|
||||||
|
|||||||
@ -1,2 +1,4 @@
|
|||||||
<div .ui.container>
|
<section>
|
||||||
|
^{allUsersWgt'}
|
||||||
|
<section>
|
||||||
^{userList}
|
^{userList}
|
||||||
|
|||||||
@ -113,6 +113,8 @@ fillDb = do
|
|||||||
, userCreated = now
|
, userCreated = now
|
||||||
, userLastLdapSynchronisation = Nothing
|
, userLastLdapSynchronisation = Nothing
|
||||||
, userCsvOptions = csvPreset # CsvPresetRFC
|
, userCsvOptions = csvPreset # CsvPresetRFC
|
||||||
|
, userSex = Just SexMale
|
||||||
|
, userShowSex = userDefaultShowSex
|
||||||
}
|
}
|
||||||
fhamann <- insert User
|
fhamann <- insert User
|
||||||
{ userIdent = "felix.hamann@campus.lmu.de"
|
{ userIdent = "felix.hamann@campus.lmu.de"
|
||||||
@ -139,6 +141,8 @@ fillDb = do
|
|||||||
, userCreated = now
|
, userCreated = now
|
||||||
, userLastLdapSynchronisation = Nothing
|
, userLastLdapSynchronisation = Nothing
|
||||||
, userCsvOptions = csvPreset # CsvPresetExcel
|
, userCsvOptions = csvPreset # CsvPresetExcel
|
||||||
|
, userSex = Just SexMale
|
||||||
|
, userShowSex = userDefaultShowSex
|
||||||
}
|
}
|
||||||
jost <- insert User
|
jost <- insert User
|
||||||
{ userIdent = "jost@tcs.ifi.lmu.de"
|
{ userIdent = "jost@tcs.ifi.lmu.de"
|
||||||
@ -164,7 +168,9 @@ fillDb = do
|
|||||||
, userNotificationSettings = def
|
, userNotificationSettings = def
|
||||||
, userCreated = now
|
, userCreated = now
|
||||||
, userLastLdapSynchronisation = Nothing
|
, userLastLdapSynchronisation = Nothing
|
||||||
|
, userSex = Just SexMale
|
||||||
, userCsvOptions = def
|
, userCsvOptions = def
|
||||||
|
, userShowSex = userDefaultShowSex
|
||||||
}
|
}
|
||||||
maxMuster <- insert User
|
maxMuster <- insert User
|
||||||
{ userIdent = "max@campus.lmu.de"
|
{ userIdent = "max@campus.lmu.de"
|
||||||
@ -191,6 +197,8 @@ fillDb = do
|
|||||||
, userCreated = now
|
, userCreated = now
|
||||||
, userLastLdapSynchronisation = Nothing
|
, userLastLdapSynchronisation = Nothing
|
||||||
, userCsvOptions = def
|
, userCsvOptions = def
|
||||||
|
, userSex = Just SexMale
|
||||||
|
, userShowSex = userDefaultShowSex
|
||||||
}
|
}
|
||||||
tinaTester <- insert $ User
|
tinaTester <- insert $ User
|
||||||
{ userIdent = "tester@campus.lmu.de"
|
{ userIdent = "tester@campus.lmu.de"
|
||||||
@ -217,6 +225,8 @@ fillDb = do
|
|||||||
, userCreated = now
|
, userCreated = now
|
||||||
, userLastLdapSynchronisation = Nothing
|
, userLastLdapSynchronisation = Nothing
|
||||||
, userCsvOptions = def
|
, userCsvOptions = def
|
||||||
|
, userSex = Just SexNotApplicable
|
||||||
|
, userShowSex = userDefaultShowSex
|
||||||
}
|
}
|
||||||
svaupel <- insert User
|
svaupel <- insert User
|
||||||
{ userIdent = "vaupel.sarah@campus.lmu.de"
|
{ userIdent = "vaupel.sarah@campus.lmu.de"
|
||||||
@ -243,6 +253,8 @@ fillDb = do
|
|||||||
, userCreated = now
|
, userCreated = now
|
||||||
, userLastLdapSynchronisation = Nothing
|
, userLastLdapSynchronisation = Nothing
|
||||||
, userCsvOptions = def
|
, userCsvOptions = def
|
||||||
|
, userSex = Just SexFemale
|
||||||
|
, userShowSex = userDefaultShowSex
|
||||||
}
|
}
|
||||||
void . repsert (TermKey summer2017) $ Term
|
void . repsert (TermKey summer2017) $ Term
|
||||||
{ termName = summer2017
|
{ termName = summer2017
|
||||||
|
|||||||
@ -275,6 +275,9 @@ instance Arbitrary CsvPreset where
|
|||||||
arbitrary = genericArbitrary
|
arbitrary = genericArbitrary
|
||||||
shrink = genericShrink
|
shrink = genericShrink
|
||||||
|
|
||||||
|
instance Arbitrary Sex where
|
||||||
|
arbitrary = genericArbitrary
|
||||||
|
|
||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = do
|
spec = do
|
||||||
|
|||||||
@ -91,6 +91,7 @@ instance Arbitrary User where
|
|||||||
userSurname <- unwords <$> sublistOf names
|
userSurname <- unwords <$> sublistOf names
|
||||||
userFirstName <- unwords <$> sublistOf names
|
userFirstName <- unwords <$> sublistOf names
|
||||||
userTitle <- fmap (pack . getPrintableString) <$> arbitrary
|
userTitle <- fmap (pack . getPrintableString) <$> arbitrary
|
||||||
|
userSex <- arbitrary
|
||||||
|
|
||||||
userMaxFavourites <- getNonNegative <$> arbitrary
|
userMaxFavourites <- getNonNegative <$> arbitrary
|
||||||
userMaxFavouriteTerms <- getNonNegative <$> arbitrary
|
userMaxFavouriteTerms <- getNonNegative <$> arbitrary
|
||||||
|
|||||||
@ -142,6 +142,7 @@ createUser adjUser = do
|
|||||||
userCreated = now
|
userCreated = now
|
||||||
userLastLdapSynchronisation = Nothing
|
userLastLdapSynchronisation = Nothing
|
||||||
userCsvOptions = def
|
userCsvOptions = def
|
||||||
|
userSex = Nothing
|
||||||
runDB . insertEntity $ adjUser User{..}
|
runDB . insertEntity $ adjUser User{..}
|
||||||
|
|
||||||
lawsCheckHspec :: Typeable a => Proxy a -> [Proxy a -> Laws] -> Spec
|
lawsCheckHspec :: Typeable a => Proxy a -> [Proxy a -> Laws] -> Spec
|
||||||
|
|||||||
Reference in New Issue
Block a user