Merge branch 'i18n' of gitlab.cip.ifi.lmu.de:jost/UniWorX into i18n

This commit is contained in:
Sarah Vaupel 2019-10-23 19:57:25 +02:00
commit 778b69dd26
24 changed files with 360 additions and 123 deletions

View File

@ -1,5 +1,6 @@
CampusIdentPlaceholder: Vorname.Nachname@campus.lmu.de
CampusIdent: Campus-Kennung
CampusPassword: Passwort
CampusPasswordPlaceholder: Passwort
CampusSubmit: Abschicken
CampusInvalidCredentials: Ungültige Logindaten

View File

@ -1,2 +1,3 @@
DummyIdent: Nutzer-Kennung
DummyIdent: Identifikation
DummyIdentPlaceholder: Identifikation
DummyNoFormData: Keine Formulardaten empfangen

View File

@ -1,2 +1,4 @@
PWHashIdent: Identifikation
PWHashPassword: Passwort
PWHashIdentPlaceholder: Identifikation
PWHashPassword: Passwort
PWHashPasswordPlaceholder: Passwort

View File

@ -73,9 +73,9 @@ Term: Semester
TermPlaceholder: W/S + vierstellige Jahreszahl
TermStartDay: Erster Tag
TermStartDayTooltip: Üblicherweise immer 1.April oder 1.Oktober
TermStartDayTooltip: Üblicherweise immer 1. April oder 1. Oktober
TermEndDay: Letzter Tag
TermEndDayTooltip: Üblicherweise immer 30.September oder 31.März
TermEndDayTooltip: Üblicherweise immer 30. September oder 31. März
TermHolidays: Feiertage
TermHolidayPlaceholder: Feiertag
TermLectureStart: Beginn Vorlesungen
@ -111,8 +111,8 @@ CourseTutorial: Tutorium
CourseSecretWrong: Falsches Passwort
CourseSecret: Zugangspasswort
CourseEditOk tid@TermId ssh@SchoolId csh@CourseShorthand: Kurs #{tid}-#{ssh}-#{csh} wurde erfolgreich geändert.
CourseNewDupShort tid@TermId ssh@SchoolId csh@CourseShorthand: Kurs #{tid}-#{ssh}-#{csh} konnte nicht erstellt werden: Es gibt bereits einen anderen Kurs mit dem Kürzel #{csh} in diesem Semester.
CourseEditDupShort tid@TermId ssh@SchoolId csh@CourseShorthand: Kurs #{tid}-#{ssh}-#{csh} konnte nicht geändert werden: Es gibt bereits einen anderen Kurs mit dem Kürzel #{csh} in diesem Semester.
CourseNewDupShort tid@TermId ssh@SchoolId csh@CourseShorthand: Kurs #{tid}-#{ssh}-#{csh} konnte nicht erstellt werden: Es gibt bereits einen anderen Kurs mit dem Kürzel #{csh} in diesem Semester und Institut.
CourseEditDupShort tid@TermId ssh@SchoolId csh@CourseShorthand: Kurs #{tid}-#{ssh}-#{csh} konnte nicht geändert werden: Es gibt bereits einen anderen Kurs mit dem Kürzel #{csh} in diesem Semester und Institut.
FFSheetName: Name
TermCourseListHeading tid@TermId: Kursübersicht #{tid}
TermSchoolCourseListHeading tid@TermId school@SchoolName: Kursübersicht #{tid} für #{school}
@ -130,7 +130,7 @@ CourseMembersCountLimited n@Int max@Int: #{n}/#{max}
CourseMembersCountOf n@Int mbNum@IntMaybe: #{n} Kursanmeldungen #{maybeToMessage " von " mbNum " möglichen"}
CourseName: Name
CourseDescription: Beschreibung
CourseDescriptionTip: Beliebiges HTML-Markup ist gestattet
CourseDescriptionTip: Beliebiges Html-Markup ist gestattet
CourseHomepageExternal: Externe Homepage
CourseShorthand: Kürzel
CourseShorthandUnique: Muss nur innerhalb Institut und Semester eindeutig sein. Wird verbatim in die Url der Kursseite übernommen.
@ -274,6 +274,8 @@ SheetSubmissionMode: Abgabe-Modus
SheetExercise: Aufgabenstellung
SheetHint: Hinweis
SheetHintFrom: Hinweis ab
SheetHintFromPlaceholder: Datum, sonst nur für Korrektoren
SheetSolutionFromPlaceholder: Datum, sonst nur für Korrektoren
SheetSolution: Lösung
SheetSolutionFrom: Lösung ab
SheetMarking: Hinweise für Korrektoren
@ -2060,4 +2062,35 @@ ProfileSubmissions: Abgaben
ProfileRemark: Hinweis
ProfileGroupSubmissionDates: Bei Gruppenabgaben wird kein Datum angezeigt, wenn Sie die Gruppenabgabe nie selbst hochgeladen haben.
ProfileCorrectorRemark: Die oberhalb angezeigte Tabelle zeigt nur prinzipielle Einteilungen als Korrektor zu einem Übungsblatt. Auch ohne Einteilung können Korrekturen einzeln zugewiesen werden, welche hier dann nicht aufgeführt werden.
ProfileCorrections: Auflistung aller zugewiesenen Korrekturen
ProfileCorrections: Auflistung aller zugewiesenen Korrekturen
GroupSizeNotNatural: „Gruppengröße“ muss eine natürliche Zahl sein
AmbiguousEmail: E-Mail Adresse nicht eindeutig
CourseDescriptionPlaceholder: Bitte mindestens die Modulbeschreibung angeben
CourseHomepageExternalPlaceholder: Optionale externe URL
PointsPlaceholder: Punktezahl
RFC1766: RFC1766-Sprachcode
TermShort: Kürzel
TermCourseCount: Kurse
TermStart: Semesteranfang
TermEnd: Semesterende
TermStartMustMatchName: Jahreszahl im Namenskürzel stimmt nicht mit Semesterbeginn überein.
TermEndMustBeAfterStart: Semester darf nicht enden, bevor es beginnt.
TermLectureEndMustBeAfterStart: Vorlesungszeit muss vor ihrem Ende anfgangen.
TermStartMustBeBeforeLectureStart: Semester muss vor der Vorlesungszeit beginnen.
TermEndMustBeAfterLectureEnd: Vorlesungszeit muss vor dem Semester enden.
AdminPageEmpty: Diese Seite soll eine Übersichtsseite für Administratoren werden. Aktuell finden sich hier nur Links zu wichtigen Administrator-Funktionalitäten.
HaveCorrectorAccess sheetName@SheetName: Sie haben Korrektor-Zugang zu #{original sheetName}.
FavouritesPlaceholder: Anzahl Favoriten
FavouritesNotNatural: Anzahl der Favoriten muss eine natürliche Zahl sein!
FavouritesSemestersPlaceholder: Anzahl Semester
FavouritesSemestersNotNatural: Anzahl der Favoriten-Semester muss eine natürliche Zahl sein!
ProfileTitle: Benutzereinstellungen
GlossaryTitle: Begriffsverzeichnis
MenuGlossary: Begriffsverzeichnis
Applicant: Bewerber
CourseParticipant: Kursteilnehmer

View File

@ -1,2 +1,146 @@
Logo: Uni2work
BtnSubmit: Submit
BtnAbort: Abort
BtnDelete: Delete
BtnRegister: Register
BtnDeregister: Deregister
BtnCourseRegister: Enrol for course
BtnCourseDeregister: Leave course
BtnCourseApply: Apply for course
BtnCourseRetractApplication: Retract application
BtnExamRegister: Enrol for exam
BtnExamDeregister: Leave exam
BtnHijack: Hijack session
BtnSave: Save
PressSaveToSave: Changes will only be saved after clicking "Save".
BtnHandIn: Hand in submission
BtnCandidatesInfer: Infer mapping
BtnCandidatesDeleteConflicts: Delete conflicts
BtnCandidatesDeleteAll: Delete all observations
BtnResetTokens: Invalidate tokens
BtnLecInvAccept: Accept
BtnLecInvDecline: Decline
BtnCorrInvAccept: Accept
BtnCorrInvDecline: Decline
BtnSubmissionsAssign: Assign submissions automatically
Aborted: Aborted
Remarks: Remarks
Registered: Registered
RegisteredSince: Registered since
NotRegistered: Note enrolled for this course
Registration: Registration
RegisterFrom: Registration starts
RegisterTo: Registration ends
DeRegUntil: Deregistration until
RegisterRetry: You haven't been enrolled. Press "Enrol for course" to enrol
CourseRegistrationInterval: Registration
CourseDirectRegistrationInterval: Direct registration
CourseDeregisterUntil time: Deregistration only until #{time}
GenericKey: Key
GenericShort: Shorthand
GenericIsNew: New
GenericHasConflict: Conflict
GenericBack: Back
GenericChange: Change
GenericNumChange: +/-
GenericMin: Min
GenericAvg: Avg
GenericMax: Max
GenericAll: All
SummerTerm year: Summer semester #{year}
WinterTerm year: Winter semester #{year}/#{succ year}
SummerTermShort year: Summer #{year}
WinterTermShort year: Winter #{year}/#{mod (succ year) 100}
PSLimitNonPositive: “pagesize” must be greater than zero
Page num: #{num}
TermsHeading: Semesters
TermCurrent: Current semester
TermEditHeading: Edit semester
TermEditTid tid: Edit semester #{tid}
TermEdited tid: Successfully edited semester #{tid}
TermNewTitle: Edit/create semester
InvalidInput: Invalid input
Term: Semester
TermPlaceholder: (W|S)<four digit year>
TermStartDay: Starting day
TermStartDayTooltip: Usually 1st of April or 1st of October
TermEndDay: Last day
TermEndDayTooltip: Usually 30th of September or 31st of March
TermHolidays: Legal holidays
TermHolidayPlaceholder: Legal holiday
TermLectureStart: Lectures start
TermLectureEnd: Lectures end
TermLectureEndTooltip: Summer semesters are usually 14 weeks; winter semesters 15
TermActive: Active
SchoolListHeading: Departments
SchoolHeading school: #{school}
LectureStart: Lectures start
Course: Course
CourseShort: Shorthand
CourseCapacity: Capacity
CourseCapacityTip: Maximum permissable number of enrollments for this course; leave empty for unlimited capacity
CourseNoCapacity: Course has reached maximum capacity
TutorialNoCapacity: Tutorial has reached maximum capacity
CourseNotEmpty: There are currently no participants enrolled for this course.
CourseRegistration: Registration
CourseRegisterOpen: Registration is open
CourseRegisterOk: Successfully enrolled for course
CourseDeregisterOk: Successfully left course
CourseApplyOk: Successfully applied for course
CourseRetractApplyOk: Successfully retracted application for course
CourseDeregisterLecturerTip: If you deregister the participant you might loose access to this data
CourseStudyFeature: Associated subject
CourseStudyFeatureTip: For information purposes only (visible to course administrators)
CourseStudyFeatureUpdated: Successfully updated associated subject
CourseStudyFeatureNone: No associated subject
CourseTutorial: Tutorial
CourseSecretWrong: Wrong password
CourseSecret: Access password
CourseEditOk tid ssh csh: Successfully edited course #{tid}-#{ssh}-#{csh}
CourseNewDupShort tid ssh csh: Could not create course #{tid}-#{ssh}-#{csh}. Another course with shorthand #{csh} already exists for the given semester and school.
CourseEditDupShort tid ssh csh: Could not edit course #{tid}-#{ssh}-#{csh}. Another course with shorthand #{csh} already exists for the given semester and school.
FFSheetName: Name
TermCourseListHeading tid: Courses #{tid}
TermSchoolCourseListHeading tid school: Courses #{tid}, #{school}
CourseListTitle: All courses
TermCourseListTitle tid: Courses #{tid}
TermSchoolCourseListTitle tid school: Courses #{tid}, #{school}
CourseNewHeading: Create new course
CourseEditHeading tid ssh csh: Edit course #{tid}-#{ssh}-#{csh}
CourseEditTitle: Edit/Create course
CourseMembers: Participants
CourseMemberOf: Participant of
CourseAssociatedWith: associated with
CourseMembersCount n: #{n}
CourseMembersCountLimited n max: #{n}/#{max}
CourseMembersCountOf n mbNum: #{n} #{maybeToMessage "of " mbNum " "}participants
CourseName: Title
CourseDescription: Description
CourseDescriptionTip: You may use arbitrary Html-Markup
CourseHomepageExternal: External homepage
CourseShorthand: Shorthand
CourseShorthandUnique: Needs to be unique within school and semester. Will be used verbatim within the url of the course page.
CourseSemester: Semester
CourseSchool: Department
CourseSchoolShort: Department
CourseSecretTip: Enrollment for this course will require the password, if set
CourseSecretFormat: Arbitrary string
CourseRegisterFromTip: When left empty students will not be able to enroll themselves
CourseRegisterToTip: May be left empty to allow enrollment indefinitely
CourseDeregisterUntilTip: Participants may deregister from immediately after registration starts up to this time. May be left empty to allow deregistration indefinitely.
CourseFilterSearch: Text search
CourseFilterRegistered: Registered
MenuLanguage: Language
LanguageChanged: Language changed successfully

1
routes
View File

@ -63,6 +63,7 @@
/info/lecturer InfoLecturerR GET !lecturer
/info/data DataProtR GET !free
/info/allocation InfoAllocationR GET !free
/info/glossary GlossaryR GET !free
/impressum ImpressumR GET !free
/version VersionR GET !free

View File

@ -13,6 +13,7 @@ import qualified Data.CaseInsensitive as CI
data DummyMessage = MsgDummyIdent
| MsgDummyIdentPlaceholder
| MsgDummyNoFormData
deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic, Typeable)
@ -24,7 +25,9 @@ dummyForm :: ( RenderMessage (HandlerSite m) FormMessage
, Button (HandlerSite m) ButtonSubmit
, MonadHandler m
) => AForm m (CI Text)
dummyForm = areq (ciField & addDatalist userList) (fslI MsgDummyIdent & noAutocomplete) Nothing
dummyForm = wFormToAForm $ do
mr <- getMessageRender
aFormToWForm $ areq (ciField & addDatalist userList) (fslpI MsgDummyIdent (mr MsgDummyIdentPlaceholder) & noAutocomplete) Nothing
where
userList = fmap mkOptionList . runDB $ withReaderT projectBackend (map toOption <$> selectList [] [Asc UserIdent] :: ReaderT SqlBackend _ [Option UserIdent])
toOption (Entity _ User{..}) = Option userDisplayName userIdent (CI.original userIdent)

View File

@ -36,6 +36,7 @@ data CampusLogin = CampusLogin
data CampusMessage = MsgCampusIdentPlaceholder
| MsgCampusIdent
| MsgCampusPassword
| MsgCampusPasswordPlaceholder
| MsgCampusSubmit
| MsgCampusInvalidCredentials
deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic, Typeable)
@ -129,7 +130,7 @@ campusForm = do
MsgRenderer mr <- getMsgRenderer
ident <- wreq ciField (fslpI MsgCampusIdent (mr MsgCampusIdentPlaceholder) & addAttr "autofocus" "") Nothing
password <- wreq passwordField (fslI MsgCampusPassword) Nothing
password <- wreq passwordField (fslpI MsgCampusPassword (mr MsgCampusPasswordPlaceholder)) Nothing
return $ CampusLogin
<$> ident

View File

@ -22,7 +22,9 @@ data HashLogin = HashLogin
} deriving (Generic, Typeable)
data PWHashMessage = MsgPWHashIdent
| MsgPWHashIdentPlaceholder
| MsgPWHashPassword
| MsgPWHashPasswordPlaceholder
deriving (Eq, Ord, Enum, Bounded, Read, Show, Generic, Typeable)
@ -30,9 +32,11 @@ hashForm :: ( RenderMessage (HandlerSite m) FormMessage
, RenderMessage (HandlerSite m) PWHashMessage
, MonadHandler m
) => AForm m HashLogin
hashForm = HashLogin
<$> areq ciField (fslpI MsgPWHashIdent "Identifikation") Nothing
<*> areq passwordField (fslpI MsgPWHashPassword "Passwort") Nothing
hashForm = wFormToAForm $ do
mr <- getMessageRender
aFormToWForm $ HashLogin
<$> areq ciField (fslpI MsgPWHashIdent (mr MsgPWHashIdentPlaceholder)) Nothing
<*> areq passwordField (fslpI MsgPWHashPassword (mr MsgPWHashPasswordPlaceholder)) Nothing
hashLogin :: forall site.

View File

@ -2182,7 +2182,9 @@ instance YesodBreadcrumbs UniWorX where
if
| mayList -> i18nCrumb MsgBreadcrumbSystemMessage $ Just MessageListR
| otherwise -> i18nCrumb MsgBreadcrumbSystemMessage $ Just HomeR
breadcrumb (MessageListR) = i18nCrumb MsgMenuMessageList $ Just AdminR
breadcrumb MessageListR = i18nCrumb MsgMenuMessageList $ Just AdminR
breadcrumb GlossaryR = i18nCrumb MsgMenuGlossary $ Just InfoR
-- breadcrumb _ = return ("Uni2work", Nothing) -- Default is no breadcrumb at all
submissionList :: TermId -> CourseShorthand -> SheetName -> UserId -> DB [E.Value SubmissionId]
@ -2480,6 +2482,14 @@ pageActions (InfoR) = [
, menuItemModal = False
, menuItemAccessCallback' = return True
}
, MenuItem
{ menuItemType = PageActionPrime
, menuItemLabel = MsgMenuGlossary
, menuItemIcon = Nothing
, menuItemRoute = SomeRoute GlossaryR
, menuItemModal = False
, menuItemAccessCallback' = return True
}
]
pageActions (VersionR) = [
MenuItem

View File

@ -33,10 +33,7 @@ getAdminR :: Handler Html
getAdminR = -- do
siteLayoutMsg MsgAdminHeading $ do
setTitleI MsgAdminHeading
[whamlet|
This shall become the Administrators' overview page.
Its current purpose is to provide links to some important admin functions
|]
i18n MsgAdminPageEmpty
-- BEGIN - Buttons needed only here
data ButtonCreate = CreateMath | CreateInf -- Dummy for Example
@ -74,7 +71,7 @@ emailTestForm = (,)
makeDemoForm :: Int -> Form (Int,Bool,Double)
makeDemoForm n = identifyForm ("adminTestForm" :: Text) $ \html -> do
(result, widget) <- flip (renderAForm FormStandard) html $ (,,)
<$> areq (minIntField n "Zahl") (fromString $ "Ganzzahl > " ++ show n) Nothing
<$> areq (minIntFieldI n ("Zahl" :: Text)) (fromString $ "Ganzzahl > " ++ show n) Nothing
<* aformSection MsgFormBehaviour
<*> areq checkBoxField "Muss nächste Zahl größer sein?" (Just True)
<*> areq doubleField "Fliesskommazahl" Nothing
@ -254,8 +251,9 @@ postAdminTestR = do
getAdminErrMsgR, postAdminErrMsgR :: Handler Html
getAdminErrMsgR = postAdminErrMsgR
postAdminErrMsgR = do
MsgRenderer mr <- getMsgRenderer
((ctResult, ctView), ctEncoding) <- runFormPost . renderAForm FormStandard $
unTextarea <$> areq textareaField (fslpI MsgErrMsgCiphertext "Ciphertext") Nothing
unTextarea <$> areq textareaField (fslpI MsgErrMsgCiphertext (mr MsgErrMsgCiphertext)) Nothing
plaintext <- formResultMaybe ctResult $ exceptT (\err -> Nothing <$ addMessageI Error err) (return . Just) . (encodedSecretBoxOpen :: Text -> ExceptT EncodedSecretBoxException Handler Value)

View File

@ -714,13 +714,14 @@ postCorrectionR tid ssh csh shn cid = do
results <- runDB $ correctionData tid ssh csh shn sub
MsgRenderer mr <- getMsgRenderer
case results of
[(Entity _ Course{..}, Entity _ Sheet{..}, Entity _ subm@Submission{..}, corrector)] -> do
let ratingComment = fmap Text.strip submissionRatingComment >>= (\c -> c <$ guard (not $ null c))
pointsForm = case sheetType of
NotGraded -> pure Nothing
_otherwise -> aopt (pointsFieldMax $ preview (_grading . _maxPoints) sheetType)
(fslpI MsgRatingPoints "Punktezahl" & setTooltip sheetType)
(fslpI MsgRatingPoints (mr MsgPointsPlaceholder) & setTooltip sheetType)
(Just submissionRatingPoints)
((corrResult, corrForm'), corrEncoding) <- runFormPost . identifyForm FIDcorrection . renderAForm FormStandard $ (,,)
@ -776,7 +777,6 @@ postCorrectionR tid ssh csh shn cid = do
addMessageI Success MsgRatingFilesUpdated
redirect $ CSubmissionR tid ssh csh shn cid CorrectionR
mr <- getMessageRender
let sheetTypeDesc = mr sheetType
heading = MsgCorrectionHead tid ssh csh shn cid
headingWgt = [whamlet|
@ -868,9 +868,10 @@ postCorrectionsCreateR = do
, optionInternalValue = sid
, optionExternalValue = toPathPiece (cID :: CryptoUUIDSheet)
}
MsgRenderer mr <- getMsgRenderer
((pseudonymRes, pseudonymWidget), pseudonymEncoding) <- runFormPost . renderAForm FormStandard $ (,)
<$> areq (selectField sheetOptions) (fslI MsgPseudonymSheet) Nothing
<*> (textToList <$> areq textareaField (fslpI MsgCorrectionPseudonyms "Pseudonyme" & setTooltip MsgCorrectionPseudonymsTip) Nothing)
<*> (textToList <$> areq textareaField (fslpI MsgCorrectionPseudonyms (mr MsgPseudonyms) & setTooltip MsgCorrectionPseudonymsTip) Nothing)
case pseudonymRes of
FormMissing -> return ()

View File

@ -256,7 +256,6 @@ makeCourseForm miButtonAction template = identifyForm FIDcourse . validateFormDB
optionalActionW' (bool mforcedJust mpopt mayChange) allocationForm' (fslI MsgCourseAllocationParticipate & setTooltip MsgCourseAllocationParticipateTip) (is _Just . cfAllocation <$> template)
-- TODO: internationalization
-- let autoUnzipInfo = [|Entpackt hochgeladene Zip-Dateien (*.zip) automatisch und fügt den Inhalt dem Stamm-Verzeichnis der Abgabe hinzu. TODO|]
(result, widget) <- flip (renderAForm FormStandard) html $ CourseForm
@ -267,9 +266,9 @@ makeCourseForm miButtonAction template = identifyForm FIDcourse . validateFormDB
& setTooltip MsgCourseShorthandUnique) (cfShort <$> template)
<*> areq (schoolFieldFor userSchools) (fslI MsgCourseSchool) (cfSchool <$> template)
<*> areq termsField (fslI MsgCourseSemester) (cfTerm <$> template)
<*> aopt htmlField (fslpI MsgCourseDescription "Bitte mindestens die Modulbeschreibung angeben"
<*> aopt htmlField (fslpI MsgCourseDescription (mr MsgCourseDescriptionPlaceholder)
& setTooltip MsgCourseDescriptionTip) (cfDesc <$> template)
<*> aopt (urlField & cfStrip) (fslpI MsgCourseHomepageExternal "Optionale externe URL")
<*> aopt (urlField & cfStrip) (fslpI MsgCourseHomepageExternal (mr MsgCourseHomepageExternalPlaceholder))
(cfLink <$> template)
<*> apopt checkBoxField (fslI MsgMaterialFree) (cfMatFree <$> template)
<* aformSection MsgCourseFormSectionRegistration

View File

@ -3,6 +3,9 @@ module Handler.Info where
import Import
import Handler.Utils
import qualified Data.Map as Map
import qualified Data.CaseInsensitive as CI
import Development.GitRev
-- | Versionsgeschichte
@ -67,3 +70,21 @@ getInfoLecturerR =
if currentTime > expiryTime
then mempty
else toWidget [whamlet| ^{iconTooltip tooltipNew (Just IconNew) False} |]
getGlossaryR :: Handler Html
getGlossaryR =
siteLayoutMsg' MsgGlossaryTitle $ do
setTitleI MsgGlossaryTitle
MsgRenderer mr <- getMsgRenderer
let
entries' = sortOn (CI.mk . view _2) $ do
(k, v) <- Map.toList entries
msg <- maybeToList $ Map.lookup k msgMap
return (k, mr msg, v)
$(widgetFile "glossary")
where
entries = $(i18nWidgetFiles "glossary")
msgMap = Map.fromList
[ ("applicant" , MsgApplicant )
, ("course-participant", MsgCourseParticipant)
]

View File

@ -79,15 +79,16 @@ instance RenderMessage UniWorX NotificationTriggerKind where
makeSettingForm :: Maybe SettingsForm -> Form SettingsForm
makeSettingForm template html = do
MsgRenderer mr <- getMsgRenderer
(result, widget) <- flip (renderAForm FormStandard) html $ SettingsForm
<$ aformSection MsgFormPersonalAppearance
<*> areq (textField & cfStrip) (fslI MsgUserDisplayName & setTooltip MsgUserDisplayNameRulesBelow) (stgDisplayName <$> template)
<*> areq (emailField & cfStrip & cfCI) (fslI MsgUserDisplayEmail & setTooltip MsgUserDisplayEmailTip) (stgDisplayEmail <$> template)
<* aformSection MsgFormCosmetics
<*> areq (natFieldI $ MsgNatField "Favoriten")
(fslpI MsgFavourites "Anzahl Favoriten" & setTooltip MsgFavouritesTip) (stgMaxFavourites <$> template)
<*> areq (natFieldI $ MsgNatField "Favoriten-Semester")
(fslpI MsgFavouriteSemesters "Anzahl Semester") (stgMaxFavouriteTerms <$> template)
<*> areq (natFieldI MsgFavouritesNotNatural)
(fslpI MsgFavourites (mr MsgFavouritesPlaceholder) & setTooltip MsgFavouritesTip) (stgMaxFavourites <$> template)
<*> areq (natFieldI MsgFavouritesSemestersNotNatural)
(fslpI MsgFavouriteSemesters (mr MsgFavouritesSemestersPlaceholder)) (stgMaxFavouriteTerms <$> template)
<*> areq (selectField . return $ mkOptionList themeList)
(fslI MsgTheme) { fsId = Just "theme-select" } (stgTheme <$> template)
<*> areq (selectField $ dateTimeFormatOptions SelFormatDateTime) (fslI MsgDateTimeFormat) (stgDateTime <$> template)
@ -318,7 +319,7 @@ postProfileR = do
tResetTime <- traverse (formatTime SelFormatDateTime) userTokensIssuedAfter
siteLayout [whamlet|_{MsgProfileFor} ^{nameWidget userDisplayName userSurname}|] $ do
setTitle . toHtml $ "Profil " <> userIdent
setTitleI MsgProfileTitle
let settingsForm =
wrapForm formWidget FormSettings
{ formMethod = POST

View File

@ -91,7 +91,7 @@ makeSheetForm msId template = identifyForm FIDsheet $ \html -> do
oldFileIds <- (return.) <$> case msId of
Nothing -> return $ partitionFileType mempty
(Just sId) -> liftHandler $ runDB $ getFtIdMap sId
mr <- getMsgRenderer
mr'@(MsgRenderer mr) <- getMsgRenderer
ctime <- ceilingQuarterHour <$> liftIO getCurrentTime
(result, widget) <- flip (renderAForm FormStandard) html $ SheetForm
<$> areq (textField & cfStrip & cfCI) (fslI MsgSheetName) (sfName <$> template)
@ -103,9 +103,9 @@ makeSheetForm msId template = identifyForm FIDsheet $ \html -> do
& setTooltip MsgSheetActiveFromTip)
(sfActiveFrom <$> template)
<*> areq utcTimeField (fslI MsgSheetActiveTo) (sfActiveTo <$> template)
<*> aopt utcTimeField (fslpI MsgSheetHintFrom "Datum, sonst nur für Korrektoren"
<*> aopt utcTimeField (fslpI MsgSheetHintFrom (mr MsgSheetHintFromPlaceholder)
& setTooltip MsgSheetHintFromTip) (sfHintFrom <$> template)
<*> aopt utcTimeField (fslpI MsgSheetSolutionFrom "Datum, sonst nur für Korrektoren"
<*> aopt utcTimeField (fslpI MsgSheetSolutionFrom (mr MsgSheetSolutionFromPlaceholder)
& setTooltip MsgSheetSolutionFromTip) (sfSolutionFrom <$> template)
<* aformSection MsgSheetFormFiles
<*> aopt (multiFileField $ oldFileIds SheetExercise) (fslI MsgSheetExercise) (sfSheetF <$> template)
@ -124,7 +124,7 @@ makeSheetForm msId template = identifyForm FIDsheet $ \html -> do
<*> aopt htmlField (fslpI MsgSheetMarking "Html") (sfMarkingText <$> template)
return $ case result of
FormSuccess sheetResult
| errorMsgs <- validateSheet mr sheetResult
| errorMsgs <- validateSheet mr' sheetResult
, not $ null errorMsgs ->
(FormFailure errorMsgs, widget)
_ -> (result, widget)
@ -923,5 +923,5 @@ postSCorrInviteR = invitationR correctorInvitationConfig
getSIsCorrR :: TermId -> SchoolId -> CourseShorthand -> SheetName -> Handler Html
-- NOTE: The route SIsCorrR is only used to verfify corrector access rights to given sheet!
getSIsCorrR _ _ _ shn = do
defaultLayout $ [whamlet|You have corrector access to #{shn}.|]
defaultLayout . i18n $ MsgHaveCorrectorAccess shn

View File

@ -23,6 +23,7 @@ postMessageR cID = do
Nothing -> (systemMessageSummary, systemMessageContent)
Just SystemMessageTranslation{..} -> (systemMessageTranslationSummary, systemMessageTranslationContent)
MsgRenderer mr <- getMsgRenderer
let
mkForm = do
((modifyRes, modifyView), modifyEnctype) <- runFormPost . identifyForm FIDSystemMessageModify . renderAForm FormStandard
@ -31,9 +32,9 @@ postMessageR cID = do
<*> aopt utcTimeField (fslI MsgSystemMessageTo) (Just systemMessageTo)
<*> areq checkBoxField (fslI MsgSystemMessageAuthenticatedOnly) (Just systemMessageAuthenticatedOnly)
<*> areq (selectField optionsFinite) (fslI MsgSystemMessageSeverity) (Just systemMessageSeverity)
<*> areq (langField False) (fslpI MsgSystemMessageLanguage "RFC1766-Sprachcode") (Just systemMessageDefaultLanguage)
<*> areq htmlField' (fslpI MsgSystemMessageContent "HTML") (Just systemMessageContent)
<*> aopt htmlField' (fslpI MsgSystemMessageSummary "HTML") (Just systemMessageSummary)
<*> areq (langField False) (fslpI MsgSystemMessageLanguage (mr MsgRFC1766)) (Just systemMessageDefaultLanguage)
<*> areq htmlField' (fslpI MsgSystemMessageContent "Html") (Just systemMessageContent)
<*> aopt htmlField' (fslpI MsgSystemMessageSummary "Html") (Just systemMessageSummary)
ts <- runDB $ selectList [ SystemMessageTranslationMessage ==. smId ] [Asc SystemMessageTranslationLanguage]
let ts' = Map.fromList $ (systemMessageTranslationLanguage . entityVal &&& id) <$> ts
@ -45,9 +46,9 @@ postMessageR cID = do
<$> fmap (Entity tId)
( SystemMessageTranslation
<$> pure systemMessageTranslationMessage
<*> areq (langField False) (fslpI MsgSystemMessageLanguage "RFC1766-Sprachcode") (Just systemMessageTranslationLanguage)
<*> areq htmlField' (fslpI MsgSystemMessageContent "HTML") (Just systemMessageTranslationContent)
<*> aopt htmlField' (fslpI MsgSystemMessageSummary "HTML") (Just systemMessageTranslationSummary)
<*> areq (langField False) (fslpI MsgSystemMessageLanguage (mr MsgRFC1766)) (Just systemMessageTranslationLanguage)
<*> areq htmlField' (fslpI MsgSystemMessageContent "Html") (Just systemMessageTranslationContent)
<*> aopt htmlField' (fslpI MsgSystemMessageSummary "Html") (Just systemMessageTranslationSummary)
)
<*> combinedButtonFieldF ""
@ -56,9 +57,9 @@ postMessageR cID = do
((addTransRes, addTransView), addTransEnctype) <- runFormPost . identifyForm FIDSystemMessageAddTranslation . renderAForm FormStandard
$ SystemMessageTranslation
<$> pure smId
<*> areq (langField False) (fslpI MsgSystemMessageLanguage "RFC1766-Sprachcode") Nothing
<*> areq htmlField' (fslpI MsgSystemMessageContent "HTML") Nothing
<*> aopt htmlField' (fslpI MsgSystemMessageSummary "HTML") Nothing
<*> areq (langField False) (fslpI MsgSystemMessageLanguage (mr MsgRFC1766)) Nothing
<*> areq htmlField' (fslpI MsgSystemMessageContent "Html") Nothing
<*> aopt htmlField' (fslpI MsgSystemMessageSummary "Html") Nothing
formResult modifyRes $ modifySystemMessage smId
@ -252,14 +253,15 @@ postMessageListR = do
FormSuccess (_, _selection) -- prop> null _selection
-> addMessageI Error MsgSystemMessageEmptySelection
MsgRenderer mr <- getMsgRenderer
((addRes, addView), addEncoding) <- runFormPost . identifyForm FIDSystemMessageAdd . renderAForm FormStandard $ SystemMessage
<$> aopt utcTimeField (fslI MsgSystemMessageFrom) Nothing
<*> aopt utcTimeField (fslI MsgSystemMessageTo) Nothing
<*> areq checkBoxField (fslI MsgSystemMessageAuthenticatedOnly) Nothing
<*> areq (selectField optionsFinite) (fslI MsgSystemMessageSeverity) Nothing
<*> areq (langField False) (fslpI MsgSystemMessageLanguage "RFC1766-Sprachcode") (Just $ NonEmpty.head appLanguages)
<*> areq htmlField' (fslpI MsgSystemMessageContent "HTML") Nothing
<*> aopt htmlField' (fslpI MsgSystemMessageSummary "HTML") Nothing
<*> areq (langField False) (fslpI MsgSystemMessageLanguage (mr MsgRFC1766)) (Just $ NonEmpty.head appLanguages)
<*> areq htmlField' (fslpI MsgSystemMessageContent "Html") Nothing
<*> aopt htmlField' (fslpI MsgSystemMessageSummary "Html") Nothing
case addRes of
FormMissing -> return ()

View File

@ -7,6 +7,8 @@ import qualified Data.Map as Map
import qualified Database.Esqueleto as E
import qualified Data.Set as Set
import qualified Control.Monad.State.Class as State
-- | Default start day of term for season,
@ -18,32 +20,15 @@ defaultDay True Summer = fromGregorian 2020 4 1
defaultDay False Summer = fromGregorian 2020 9 30
validateTerm :: Term -> [Text]
validateTerm Term{..} =
[ msg | (False, msg) <-
[ --startOk
( termStart `withinTerm` termName
, "Jahreszahl im Namenskürzel stimmt nicht mit Semesterbeginn überein."
)
, -- endOk
( termStart < termEnd
, "Semester darf nicht enden, bevor es begann."
)
, -- startOk
( termLectureStart < termLectureEnd
, "Vorlesungszeit muss vor ihrem Ende anfgangen."
)
, -- lecStartOk
( termStart <= termLectureStart
, "Semester muss vor der Vorlesungszeit beginnen."
)
, -- lecEndOk
( termEnd >= termLectureEnd
, "Vorlesungszeit muss vor dem Semester enden."
)
] ]
validateTerm :: (MonadHandler m, HandlerSite m ~ UniWorX)
=> FormValidator Term m ()
validateTerm = do
Term{..} <- State.get
guardValidation MsgTermStartMustMatchName $ termStart `withinTerm` termName
guardValidation MsgTermEndMustBeAfterStart $ termStart < termEnd
guardValidation MsgTermLectureEndMustBeAfterStart $ termLectureStart < termLectureEnd
guardValidation MsgTermStartMustBeBeforeLectureStart $ termStart <= termLectureStart
guardValidation MsgTermEndMustBeAfterLectureEnd $ termEnd >= termLectureEnd
getTermShowR :: Handler TypedContent
@ -66,22 +51,22 @@ getTermShowR = do
provideRep $ toJSON . map fst <$> runDB (E.select $ E.from termData)
provideRep $ do
let colonnadeTerms = widgetColonnade $ mconcat
[ sortable (Just "term-id") "Kürzel" $ \(Entity tid _, _) -> anchorCell
[ sortable (Just "term-id") (i18nCell MsgTermShort) $ \(Entity tid _, _) -> anchorCell
(TermCourseListR tid)
[whamlet|#{toPathPiece tid}|]
, sortable (Just "lecture-start") (i18nCell MsgLectureStart) $ \(Entity _ Term{..},_) ->
cell $ formatTime SelFormatDate termLectureStart >>= toWidget
, sortable (Just "lecture-end") "Ende Vorlesungen" $ \(Entity _ Term{..},_) ->
, sortable (Just "lecture-end") (i18nCell MsgTermLectureEnd) $ \(Entity _ Term{..},_) ->
cell $ formatTime SelFormatDate termLectureEnd >>= toWidget
, sortable Nothing "Aktiv" $ \(Entity _ Term{..},_) ->
, sortable Nothing (i18nCell MsgTermActive) $ \(Entity _ Term{..},_) ->
tickmarkCell termActive
, sortable Nothing "Kurse" $ \(_, E.Value numCourses) ->
, sortable Nothing (i18nCell MsgTermCourseCount) $ \(_, E.Value numCourses) ->
cell [whamlet|_{MsgNumCourses numCourses}|]
, sortable (Just "start") "Semesteranfang" $ \(Entity _ Term{..},_) ->
, sortable (Just "start") (i18nCell MsgTermStart) $ \(Entity _ Term{..},_) ->
cell $ formatTime SelFormatDate termStart >>= toWidget
, sortable (Just "end") "Semesterende" $ \(Entity _ Term{..},_) ->
, sortable (Just "end") (i18nCell MsgTermEnd) $ \(Entity _ Term{..},_) ->
cell $ formatTime SelFormatDate termEnd >>= toWidget
, sortable Nothing "Feiertage im Semester" $ \(Entity _ Term{..},_) ->
, sortable Nothing (i18nCell MsgTermHolidays) $ \(Entity _ Term{..},_) ->
cell $ do
termHolidays' <- mapM (formatTime SelFormatDate) termHolidays
[whamlet|
@ -248,7 +233,7 @@ termToTemplate (Just Term{..}) = TermFormTemplate
}
newTermForm :: TermFormTemplate -> Form Term
newTermForm template html = do
newTermForm template = validateForm validateTerm $ \html -> do
mr <- getMessageRender
let
tidForm
@ -264,7 +249,7 @@ newTermForm template html = do
(fslI MsgTermHolidays & setTooltip MsgMassInputTip)
True
(tftHolidays template)
(result, widget) <- flip (renderAForm FormStandard) html $ Term
flip (renderAForm FormStandard) html $ Term
<$> tidForm
<*> areq dayField (fslI MsgTermStartDay & setTooltip MsgTermStartDayTooltip) (tftStart template)
<*> areq dayField (fslI MsgTermEndDay & setTooltip MsgTermEndDayTooltip) (tftEnd template)
@ -272,24 +257,3 @@ newTermForm template html = do
<*> areq dayField (fslI MsgTermLectureStart) (tftLectureStart template)
<*> areq dayField (fslI MsgTermLectureEnd & setTooltip MsgTermLectureEndTooltip) (tftLectureEnd template)
<*> areq checkBoxField (fslI MsgTermActive) (tftActive template)
return $ case result of
FormSuccess termResult
| errorMsgs <- validateTerm termResult
, not $ null errorMsgs ->
(FormFailure errorMsgs,
[whamlet|
<div class="alert alert-danger">
<div class="alert__content">
<h4> Fehler:
<ul>
$forall errmsg <- errorMsgs
<li> #{errmsg}
^{widget}
|]
)
_ -> (result, widget)
{-
where
set :: Text -> FieldSettings site
set = bfs
-}

View File

@ -284,21 +284,12 @@ htmlField' = htmlField
natFieldI :: (Monad m, Integral i, RenderMessage (HandlerSite m) msg, RenderMessage (HandlerSite m) FormMessage) => msg -> Field m i
natFieldI msg = convertField fromInteger toInteger $ checkBool (>= 0) msg $ intMinField 0
natField :: (Monad m, Integral i, RenderMessage (HandlerSite m) FormMessage) => Text -> Field m i
natField d = convertField fromInteger toInteger $ checkBool (>= 0) (T.append d " muss eine natürliche Zahl sein.") $ intMinField 0
natIntField ::(Monad m, RenderMessage (HandlerSite m) FormMessage) => Text -> Field m Integer
natIntField = natField
posIntField :: (Monad m, Integral i, RenderMessage (HandlerSite m) FormMessage) => Text -> Field m i
posIntField d = convertField fromInteger toInteger $ checkBool (> 0) (T.append d " muss eine positive Zahl sein.") $ intMinField 1
posIntFieldI :: (Monad m, Integral i, RenderMessage (HandlerSite m) msg, RenderMessage (HandlerSite m) FormMessage) => msg -> Field m i
posIntFieldI msg = convertField fromInteger toInteger $ checkBool (> 0) msg $ intMinField 0
-- | Field to request integral number > 'm'
minIntField :: (Monad m, Integral i, Show i, RenderMessage (HandlerSite m) FormMessage) => i -> Text -> Field m i
minIntField m d = checkBool (> m) (T.concat [d," muss größer als ", T.pack $ show m, " sein."]) $ intMinField m
minIntFieldI :: (Monad m, Integral i, Show i, RenderMessage (HandlerSite m) FormMessage, RenderMessage (HandlerSite m) msg) => i -> msg -> Field m i
minIntFieldI m msg = checkBool (> m) msg $ intMinField m
pointsField :: (Monad m, HandlerSite m ~ UniWorX) => Field m Points
pointsField = pointsFieldMinMax (Just 0) Nothing
@ -831,7 +822,7 @@ sheetGroupAFormReq fs template = multiActionA selOptions fs (classify' <$> templ
where
selOptions = Map.fromList
[ ( Arbitrary', Arbitrary
<$> apreq (natField "Gruppengröße") (fslI MsgSheetGroupMaxGroupsize & noValidate) (preview _maxParticipants =<< template)
<$> apreq (natFieldI MsgGroupSizeNotNatural) (fslI MsgSheetGroupMaxGroupsize & noValidate) (preview _maxParticipants =<< template)
)
, ( RegisteredGroups', pure RegisteredGroups )
, ( NoGroups', pure NoGroups )
@ -861,6 +852,10 @@ dayTimeField fs mutc = do
| otherwise = (Nothing,Nothing)
-}
fieldTimeFormat :: String
-- fieldTimeFormat = "%e.%m.%y %k:%M"
fieldTimeFormat = "%Y-%m-%dT%H:%M:%S"
localTimeField :: (MonadHandler m, HandlerSite m ~ UniWorX) => Field m LocalTime
localTimeField = Field
{ fieldParse = parseHelperGen readTime
@ -873,11 +868,7 @@ localTimeField = Field
, fieldEnctype = UrlEncoded
}
where
fieldTimeFormat :: String
--fieldTimeFormat = "%e.%m.%y %k:%M"
fieldTimeFormat = "%Y-%m-%dT%H:%M:%S"
-- `defaultTimeLocale` is okay here, since `fieldTimeFormat` does not contain any
-- `defaultTimeLocale` is okay here, since `fieldTimeFormat` does not contain any words
readTime :: Text -> Either UniWorXMessage LocalTime
readTime t =
case parseTimeM True defaultTimeLocale fieldTimeFormat (T.unpack t) of
@ -1149,7 +1140,7 @@ multiUserField onlySuggested suggestions = Field{..}
case dbRes of
[] -> return $ Left email
[E.Value uid] -> return $ Right uid
_other -> throwE $ SomeMessage ("Ambiguous e-mail addr" :: Text)
_other -> throwE $ SomeMessage MsgAmbiguousEmail
examResultField :: forall m res.
( MonadHandler m

View File

@ -9,6 +9,11 @@ import Language.Haskell.TH.Syntax (qRunIO)
import qualified Data.List as List
import qualified Data.List.NonEmpty as NonEmpty
import qualified Data.Set as Set
import qualified Data.Map as Map
import qualified Data.Text as Text
import System.Directory (listDirectory)
import System.FilePath.Posix (takeBaseName)
@ -40,4 +45,24 @@ i18nWidgetFile basename = do
[ funD ws $ [ clause [litP $ stringL l] (normalB . widgetFile $ "i18n" </> basename </> l) []
| l <- unpack <$> NonEmpty.toList availableTranslations' -- One function definition for every available language
] ++ [ clause [wildP] (normalB [e| error "selectLanguage returned an invalid translation" |]) [] ] -- Fallback mostly there so compiler does not complain about non-exhaustive pattern match
] [e|selectLanguage availableTranslations' >>= $(varE ws)|]
] [e|selectLanguage availableTranslations' >>= $(varE ws)|]
i18nWidgetFiles :: FilePath -> Q Exp
i18nWidgetFiles basename = do
let i18nDirectory = "templates" </> "i18n" </> basename
availableFiles <- qRunIO $ listDirectory i18nDirectory
let fileKinds' = fmap (pack . dropExtension . takeBaseName &&& toTranslation . pack . takeBaseName) availableFiles
fileKinds :: Map Text [Text]
fileKinds = sortWith (NTop . flip List.elemIndex (NonEmpty.toList appLanguages)) . Set.toList <$> Map.fromListWith Set.union [ (kind, Set.singleton l) | (kind, Just l) <- fileKinds' ]
toTranslation fName = listToMaybe . sortOn length . mapMaybe (flip Text.stripPrefix fName) $ map fst fileKinds'
availableTranslations' <- iforM fileKinds $ \kind -> maybe (fail $ "" <> i18nDirectory <> " has no translations for " <> unpack kind <> "") return . NonEmpty.nonEmpty
-- Dispatch to correct language (depending on user settings via `selectLanguage`) at run time
ws <- newName "ws" -- Name for dispatch function
letE
[ funD ws $ [ clause [litP $ stringL kind, litP $ stringL l] (normalB . widgetFile $ "i18n" </> basename </> kind <.> l) []
| (unpack -> kind, ls) <- Map.toList availableTranslations'
, l <- unpack <$> NonEmpty.toList ls
] ++ [ clause [wildP, wildP] (normalB [e| error "selectLanguage returned an invalid translation" |]) [] ] -- Fallback mostly there so compiler does not complain about non-exhaustive pattern match
] [e|imap (\kind ls -> selectLanguage ls >>= $(varE ws) kind) availableTranslations'|]

View File

@ -0,0 +1,14 @@
.glossary
dt, .dt
font-weight: 600
&.sec
font-style: italic
font-size: 0.9rem
font-weight: 600
color: var(--color-fontsec)
dd, .dd
margin-left: 12px
dd + dt, .dd + dt, dd + .dt, .dd + .dt
margin-top: 17px

View File

@ -0,0 +1,5 @@
$newline never
<dl .glossary>
$forall (term, rTerm, wgt) <- entries'
<dt ##{term}>#{rTerm}
^{wgt}

View File

@ -0,0 +1,8 @@
$newline never
<dt .sec>
Bewerbung
<dt .sec>
Zu einem Kurs bewerben
<dd>
Studierende können eine Bewerbung für einen Kurs hinterlegen.<br />
Wird die Bewerbung akzeptiert (oder der Studierende dem Kurs zugeteilt) wird der Studierende zusätzlich Kursteilnehmer.

View File

@ -0,0 +1,8 @@
$newline never
<dt .sec>
Zu einem Kurs anmelden
<dt .sec>
Von einem Kurs abmelden
<dd>
Studierende, die sich explizit mit einem Kurs assoziiert haben (oder ihm zugeteilt wurden).<br />
Haben Zugriff auf Material und Teile des Kurses (Anmeldungen zu Tutorien, Prüfungen, ...)