Merge branch 'master' of gitlab.cip.ifi.lmu.de:jost/UniWorX into feat/multi-file-field
This commit is contained in:
commit
9a26d17c5e
@ -18,11 +18,10 @@
|
|||||||
- i18n der
|
- i18n der
|
||||||
Links -> MenuItems verwenden wie bisher
|
Links -> MenuItems verwenden wie bisher
|
||||||
Page Titles -> setTitleI
|
Page Titles -> setTitleI
|
||||||
Buttons?
|
Buttons? -> Kann leicht geändert werden!
|
||||||
Was ist mit einfachen Text Feldern, z.B. die Beschriftung von Knöpfen wie in Handler.Course.getCourseListTermR, Zeile 66 "pageActions" für menuItemLabel?
|
Was ist mit einfachen Text Feldern, z.B. die Beschriftung von Knöpfen wie in Handler.Course.getCourseListTermR, Zeile 66 "pageActions" für menuItemLabel?
|
||||||
|
|
||||||
** Page pageActions
|
** Page pageActions - Berechtigungen prüfen?
|
||||||
- Berechtigungen prüfen?
|
|
||||||
=> Eigener Constructor statt NavbarLeft/Right?!
|
=> Eigener Constructor statt NavbarLeft/Right?!
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
38
fill-db.hs
38
fill-db.hs
@ -87,19 +87,17 @@ main = db $ do
|
|||||||
, courseTermId = TermKey summer2018
|
, courseTermId = TermKey summer2018
|
||||||
, courseSchoolId = ifi
|
, courseSchoolId = ifi
|
||||||
, courseCapacity = Just 20
|
, courseCapacity = Just 20
|
||||||
, courseCreated = now
|
|
||||||
, courseChanged = now
|
|
||||||
, courseCreatedBy = gkleen
|
|
||||||
, courseChangedBy = gkleen
|
|
||||||
, courseHasRegistration = True
|
, courseHasRegistration = True
|
||||||
, courseRegisterFrom = Just now
|
, courseRegisterFrom = Just now
|
||||||
, courseRegisterTo = Just ((3600 * 24 * 60) `addUTCTime` now )
|
, courseRegisterTo = Just ((3600 * 24 * 60) `addUTCTime` now )
|
||||||
}
|
}
|
||||||
|
insert_ $ CourseEdit jost now ffp
|
||||||
void . insert $ DegreeCourse ifiBsc ffp
|
void . insert $ DegreeCourse ifiBsc ffp
|
||||||
void . insert $ DegreeCourse ifiMsc ffp
|
void . insert $ DegreeCourse ifiMsc ffp
|
||||||
void . insert $ Lecturer gkleen ffp
|
void . insert $ Lecturer gkleen ffp
|
||||||
void . insert $ Corrector gkleen ffp (ByProportion 1)
|
insert_ $ Corrector gkleen ffp (ByProportion 1)
|
||||||
void . insert $ Sheet ffp "Blatt 1" Nothing NotGraded Nothing now now Nothing Nothing now now gkleen gkleen
|
sheetkey <- insert $ Sheet ffp "Blatt 1" Nothing NotGraded NoGroups Nothing Nothing now now Nothing Nothing
|
||||||
|
insert_ $ SheetEdit gkleen now sheetkey
|
||||||
-- EIP
|
-- EIP
|
||||||
eip <- insert Course
|
eip <- insert Course
|
||||||
{ courseName = "Einführung in die Programmierung"
|
{ courseName = "Einführung in die Programmierung"
|
||||||
@ -109,14 +107,11 @@ main = db $ do
|
|||||||
, courseTermId = TermKey summer2017
|
, courseTermId = TermKey summer2017
|
||||||
, courseSchoolId = ifi
|
, courseSchoolId = ifi
|
||||||
, courseCapacity = Just 20
|
, courseCapacity = Just 20
|
||||||
, courseCreated = now
|
|
||||||
, courseChanged = now
|
|
||||||
, courseCreatedBy = fhamann
|
|
||||||
, courseChangedBy = fhamann
|
|
||||||
, courseHasRegistration = False
|
, courseHasRegistration = False
|
||||||
, courseRegisterFrom = Nothing
|
, courseRegisterFrom = Nothing
|
||||||
, courseRegisterTo = Nothing
|
, courseRegisterTo = Nothing
|
||||||
}
|
}
|
||||||
|
insert_ $ CourseEdit fhamann now eip
|
||||||
void . insert $ DegreeCourse ifiBsc eip
|
void . insert $ DegreeCourse ifiBsc eip
|
||||||
void . insert $ DegreeCourse ifiMsc eip
|
void . insert $ DegreeCourse ifiMsc eip
|
||||||
void . insert $ Lecturer fhamann eip
|
void . insert $ Lecturer fhamann eip
|
||||||
@ -129,14 +124,11 @@ main = db $ do
|
|||||||
, courseTermId = TermKey summer2018
|
, courseTermId = TermKey summer2018
|
||||||
, courseSchoolId = ifi
|
, courseSchoolId = ifi
|
||||||
, courseCapacity = Just 20
|
, courseCapacity = Just 20
|
||||||
, courseCreated = now
|
|
||||||
, courseChanged = now
|
|
||||||
, courseCreatedBy = fhamann
|
|
||||||
, courseChangedBy = fhamann
|
|
||||||
, courseHasRegistration = True
|
, courseHasRegistration = True
|
||||||
, courseRegisterFrom = Just now
|
, courseRegisterFrom = Just now
|
||||||
, courseRegisterTo = Just ((3600 * 24 * 60) `addUTCTime` now )
|
, courseRegisterTo = Just ((3600 * 24 * 60) `addUTCTime` now )
|
||||||
}
|
}
|
||||||
|
insert_ $ CourseEdit fhamann now ixd
|
||||||
void . insert $ DegreeCourse ifiBsc ixd
|
void . insert $ DegreeCourse ifiBsc ixd
|
||||||
void . insert $ Lecturer fhamann ixd
|
void . insert $ Lecturer fhamann ixd
|
||||||
-- concept development
|
-- concept development
|
||||||
@ -148,14 +140,11 @@ main = db $ do
|
|||||||
, courseTermId = TermKey winter2017
|
, courseTermId = TermKey winter2017
|
||||||
, courseSchoolId = ifi
|
, courseSchoolId = ifi
|
||||||
, courseCapacity = Just 30
|
, courseCapacity = Just 30
|
||||||
, courseCreated = now
|
|
||||||
, courseChanged = now
|
|
||||||
, courseCreatedBy = fhamann
|
|
||||||
, courseChangedBy = fhamann
|
|
||||||
, courseHasRegistration = False
|
, courseHasRegistration = False
|
||||||
, courseRegisterFrom = Nothing
|
, courseRegisterFrom = Nothing
|
||||||
, courseRegisterTo = Nothing
|
, courseRegisterTo = Nothing
|
||||||
}
|
}
|
||||||
|
insert_ $ CourseEdit fhamann now ux3
|
||||||
void . insert $ DegreeCourse ifiBsc ux3
|
void . insert $ DegreeCourse ifiBsc ux3
|
||||||
void . insert $ Lecturer fhamann ux3
|
void . insert $ Lecturer fhamann ux3
|
||||||
-- promo
|
-- promo
|
||||||
@ -167,14 +156,11 @@ main = db $ do
|
|||||||
, courseTermId = TermKey summer2017
|
, courseTermId = TermKey summer2017
|
||||||
, courseSchoolId = ifi
|
, courseSchoolId = ifi
|
||||||
, courseCapacity = Just 50
|
, courseCapacity = Just 50
|
||||||
, courseCreated = now
|
|
||||||
, courseChanged = now
|
|
||||||
, courseCreatedBy = jost
|
|
||||||
, courseChangedBy = jost
|
|
||||||
, courseHasRegistration = False
|
, courseHasRegistration = False
|
||||||
, courseRegisterFrom = Nothing
|
, courseRegisterFrom = Nothing
|
||||||
, courseRegisterTo = Nothing
|
, courseRegisterTo = Nothing
|
||||||
}
|
}
|
||||||
|
insert_ $ CourseEdit jost now pmo
|
||||||
void . insert $ DegreeCourse ifiBsc pmo
|
void . insert $ DegreeCourse ifiBsc pmo
|
||||||
void . insert $ Lecturer jost pmo
|
void . insert $ Lecturer jost pmo
|
||||||
-- datenbanksysteme
|
-- datenbanksysteme
|
||||||
@ -186,13 +172,11 @@ main = db $ do
|
|||||||
, courseTermId = TermKey summer2018
|
, courseTermId = TermKey summer2018
|
||||||
, courseSchoolId = ifi
|
, courseSchoolId = ifi
|
||||||
, courseCapacity = Just 50
|
, courseCapacity = Just 50
|
||||||
, courseCreated = now
|
|
||||||
, courseChanged = now
|
|
||||||
, courseCreatedBy = jost
|
|
||||||
, courseChangedBy = jost
|
|
||||||
, courseHasRegistration = False
|
, courseHasRegistration = False
|
||||||
, courseRegisterFrom = Nothing
|
, courseRegisterFrom = Nothing
|
||||||
, courseRegisterTo = Nothing
|
, courseRegisterTo = Nothing
|
||||||
}
|
}
|
||||||
|
insert_ $ CourseEdit gkleen now dbs
|
||||||
void . insert $ DegreeCourse ifiBsc dbs
|
void . insert $ DegreeCourse ifiBsc dbs
|
||||||
void . insert $ Lecturer jost dbs
|
void . insert $ Lecturer gkleen dbs
|
||||||
|
void . insert $ Lecturer jost dbs
|
||||||
|
|||||||
47
models
47
models
@ -61,14 +61,14 @@ Course
|
|||||||
termId TermId
|
termId TermId
|
||||||
schoolId SchoolId
|
schoolId SchoolId
|
||||||
capacity Int Maybe
|
capacity Int Maybe
|
||||||
created UTCTime
|
|
||||||
changed UTCTime
|
|
||||||
createdBy UserId
|
|
||||||
changedBy UserId
|
|
||||||
hasRegistration Bool -- canRegisterNow = hasRegistration && maybe False (<= currentTime) registerFrom && maybe True (>= currentTime) registerTo
|
hasRegistration Bool -- canRegisterNow = hasRegistration && maybe False (<= currentTime) registerFrom && maybe True (>= currentTime) registerTo
|
||||||
registerFrom UTCTime Maybe
|
registerFrom UTCTime Maybe
|
||||||
registerTo UTCTime Maybe
|
registerTo UTCTime Maybe
|
||||||
CourseTermShort termId shorthand
|
CourseTermShort termId shorthand
|
||||||
|
CourseEdit
|
||||||
|
user UserId
|
||||||
|
time UTCTime
|
||||||
|
course CourseId
|
||||||
Lecturer
|
Lecturer
|
||||||
userId UserId
|
userId UserId
|
||||||
courseId CourseId
|
courseId CourseId
|
||||||
@ -103,15 +103,11 @@ Sheet
|
|||||||
activeTo UTCTime
|
activeTo UTCTime
|
||||||
hintFrom UTCTime Maybe
|
hintFrom UTCTime Maybe
|
||||||
solutionFrom UTCTime Maybe
|
solutionFrom UTCTime Maybe
|
||||||
created UTCTime -- delete
|
|
||||||
changed UTCTime -- delete
|
|
||||||
createdBy UserId -- delete
|
|
||||||
changedBy UserId -- delete
|
|
||||||
CourseSheet courseId name
|
CourseSheet courseId name
|
||||||
SheetEdit
|
SheetEdit
|
||||||
sheet SheetId
|
|
||||||
user UserId
|
user UserId
|
||||||
time UTCTime
|
time UTCTime
|
||||||
|
sheet SheetId
|
||||||
SheetFile
|
SheetFile
|
||||||
sheetId SheetId
|
sheetId SheetId
|
||||||
fileId FileId
|
fileId FileId
|
||||||
@ -128,11 +124,11 @@ Submission
|
|||||||
ratingComment Text Maybe
|
ratingComment Text Maybe
|
||||||
ratingBy UserId Maybe
|
ratingBy UserId Maybe
|
||||||
ratingTime UTCTime Maybe
|
ratingTime UTCTime Maybe
|
||||||
created UTCTime
|
|
||||||
changed UTCTime
|
|
||||||
createdBy UserId
|
|
||||||
changedBy UserId
|
|
||||||
deriving Show
|
deriving Show
|
||||||
|
SubmissionEdit
|
||||||
|
user UserId
|
||||||
|
time UTCTime
|
||||||
|
submission SubmissionId
|
||||||
SubmissionFile
|
SubmissionFile
|
||||||
submissionId SubmissionId
|
submissionId SubmissionId
|
||||||
fileId FileId
|
fileId FileId
|
||||||
@ -147,10 +143,10 @@ SubmissionUser
|
|||||||
SubmissionGroup
|
SubmissionGroup
|
||||||
courseId CourseId
|
courseId CourseId
|
||||||
name Text
|
name Text
|
||||||
created UTCTime
|
SubmissionGroupEdit
|
||||||
changed UTCTime
|
user UserId
|
||||||
createdBy UserId
|
time UTCTime
|
||||||
changedBy UserId
|
submissionGroup SubmissionGroupId
|
||||||
SubmissionGroupUser
|
SubmissionGroupUser
|
||||||
submissionGroupId SubmissionGroupId
|
submissionGroupId SubmissionGroupId
|
||||||
userId UserId
|
userId UserId
|
||||||
@ -169,13 +165,12 @@ Booking
|
|||||||
end UTCTime
|
end UTCTime
|
||||||
weekly Bool
|
weekly Bool
|
||||||
exceptions [Day] -- only if weekly, begin in exception
|
exceptions [Day] -- only if weekly, begin in exception
|
||||||
created UTCTime
|
|
||||||
changed UTCTime
|
|
||||||
createdBy UserId
|
|
||||||
changedBy UserId
|
|
||||||
|
|
||||||
bookedFor RoomForId
|
bookedFor RoomForId
|
||||||
room RoomId
|
room RoomId
|
||||||
|
BookingEdit
|
||||||
|
user UserId
|
||||||
|
time UTCTime
|
||||||
|
boooking BookingId
|
||||||
Room
|
Room
|
||||||
name Text
|
name Text
|
||||||
capacity Int Maybe
|
capacity Int Maybe
|
||||||
@ -201,10 +196,10 @@ Exam
|
|||||||
deregistrationEnd UTCTime
|
deregistrationEnd UTCTime
|
||||||
ratingVisible Bool
|
ratingVisible Bool
|
||||||
statisticsVisible Bool
|
statisticsVisible Bool
|
||||||
created UTCTime
|
ExamEdit
|
||||||
changed UTCTime
|
user UserId
|
||||||
createdBy UserId
|
time UTCTime
|
||||||
changedBy UserId
|
exam ExamId
|
||||||
ExamUser
|
ExamUser
|
||||||
userId UserId
|
userId UserId
|
||||||
examId ExamId
|
examId ExamId
|
||||||
|
|||||||
39
routes
39
routes
@ -6,36 +6,29 @@
|
|||||||
|
|
||||||
/ HomeR GET POST
|
/ HomeR GET POST
|
||||||
/profile ProfileR GET
|
/profile ProfileR GET
|
||||||
/users UsersR GET
|
/users UsersR GET !adminAny
|
||||||
|
|
||||||
/term TermShowR GET
|
/term TermShowR GET
|
||||||
/term/edit TermEditR GET POST
|
/term/edit TermEditR GET POST !adminAny
|
||||||
/term/#TermId/edit TermEditExistR GET
|
/term/#TermId/edit TermEditExistR GET !adminAny
|
||||||
|
|
||||||
/course/ CourseListR GET
|
/course/ CourseListR GET
|
||||||
!/course/new CourseNewR GET POST
|
!/course/new CourseNewR GET POST !lecturerAny
|
||||||
!/course/#TermId CourseListTermR GET
|
!/course/#TermId CourseListTermR GET
|
||||||
|
/course/#TermId/#Text CourseR:
|
||||||
|
/show CourseShowR GET POST
|
||||||
|
/edit CourseEditR GET POST !lecturer
|
||||||
|
|
||||||
-- /course/#TermId/#Text CourseR !tag:
|
/ex SheetR !registered:
|
||||||
-- /edit CourseEditR GET POST
|
/ SheetListR GET
|
||||||
-- /show CourseShowR GET POST -- CourseR tid csh CourseShowR
|
/#Text/show SheetShowR GET !time
|
||||||
-- /ex/#Text SheetR: !registered
|
/#Text/#SheetFileType/#FilePath SheetFileR GET !time
|
||||||
-- /show
|
/new SheetNewR GET POST !lecturer
|
||||||
-- /edit -- CourseR tid csg (SheetR csh SheetEditR)
|
/#Text/edit SheetEditR GET POST !lecturer
|
||||||
-- /delete
|
/#Text/delete SheetDelR GET POST !lecturer
|
||||||
|
|
||||||
/course/#TermId/#Text/edit CourseEditR GET
|
|
||||||
/course/#TermId/#Text/show CourseShowR GET POST
|
|
||||||
|
|
||||||
|
|
||||||
|
-- TODO below
|
||||||
/course/#TermId/#Text/ex/ SheetListR GET
|
|
||||||
/course/#TermId/#Text/ex/#Text/show SheetShowR GET
|
|
||||||
/course/#TermId/#Text/ex/#Text/#SheetFileType/#FilePath SheetFileR GET
|
|
||||||
/course/#TermId/#Text/ex/new SheetNewR GET POST
|
|
||||||
/course/#TermId/#Text/ex/#Text/edit SheetEditR GET POST
|
|
||||||
/course/#TermId/#Text/ex/#Text/delete SheetDelR GET POST
|
|
||||||
|
|
||||||
/submission SubmissionListR GET POST
|
/submission SubmissionListR GET POST
|
||||||
/submission/#CryptoUUIDSubmission SubmissionR GET POST
|
/submission/#CryptoUUIDSubmission SubmissionR GET POST
|
||||||
/submissions.zip SubmissionDownloadMultiArchiveR POST
|
/submissions.zip SubmissionDownloadMultiArchiveR POST
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
{-# LANGUAGE MultiParamTypeClasses #-}
|
{-# LANGUAGE MultiParamTypeClasses #-}
|
||||||
{-# LANGUAGE TypeFamilies #-}
|
{-# LANGUAGE TypeFamilies #-}
|
||||||
{-# LANGUAGE ViewPatterns #-}
|
{-# LANGUAGE ViewPatterns #-}
|
||||||
|
{-# LANGUAGE PatternSynonyms #-}
|
||||||
{-# LANGUAGE DataKinds #-}
|
{-# LANGUAGE DataKinds #-}
|
||||||
{-# LANGUAGE RecordWildCards #-}
|
{-# LANGUAGE RecordWildCards #-}
|
||||||
{-# LANGUAGE NamedFieldPuns #-}
|
{-# LANGUAGE NamedFieldPuns #-}
|
||||||
@ -53,6 +54,9 @@ import System.FilePath
|
|||||||
|
|
||||||
import Handler.Utils.Templates
|
import Handler.Utils.Templates
|
||||||
|
|
||||||
|
-- infixl 9 :$:
|
||||||
|
-- pattern a :$: b = a b
|
||||||
|
|
||||||
-- | The foundation datatype for your application. This can be a good place to
|
-- | The foundation datatype for your application. This can be a good place to
|
||||||
-- keep settings and values requiring initialization before your application
|
-- keep settings and values requiring initialization before your application
|
||||||
-- starts running, such as database connections. Every handler will have
|
-- starts running, such as database connections. Every handler will have
|
||||||
@ -80,7 +84,9 @@ data UniWorX = UniWorX
|
|||||||
-- type Widget = WidgetT UniWorX IO ()
|
-- type Widget = WidgetT UniWorX IO ()
|
||||||
mkYesodData "UniWorX" $(parseRoutesFile "routes")
|
mkYesodData "UniWorX" $(parseRoutesFile "routes")
|
||||||
|
|
||||||
type DB a = YesodDB UniWorX a
|
-- Pattern Synonyms for convenience
|
||||||
|
pattern CSheetR tid csh ptn = CourseR tid csh (SheetR ptn)
|
||||||
|
|
||||||
|
|
||||||
data MenuItem = MenuItem
|
data MenuItem = MenuItem
|
||||||
{ menuItemLabel :: Text
|
{ menuItemLabel :: Text
|
||||||
@ -89,13 +95,16 @@ data MenuItem = MenuItem
|
|||||||
, menuItemAccessCallback :: Handler Bool
|
, menuItemAccessCallback :: Handler Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
data MenuTypes
|
data MenuTypes -- Semantische Rolle:
|
||||||
= NavbarAside { menuItem :: MenuItem }
|
= NavbarAside { menuItem :: MenuItem } -- TODO
|
||||||
| NavbarRight { menuItem :: MenuItem }
|
| NavbarExtra { menuItem :: MenuItem } -- TODO
|
||||||
| NavbarExtra { menuItem :: MenuItem }
|
| NavbarRight { menuItem :: MenuItem } -- Generell, nahezu immer sichtbar
|
||||||
| NavbarSecondary { menuItem :: MenuItem }
|
| NavbarSecondary { menuItem :: MenuItem } -- Generell, nahezu immer sichtbar
|
||||||
|
| PageActionPrime { menuItem :: MenuItem } -- Seitenspezifische Aktion, häufig
|
||||||
|
| PageActionSecondary { menuItem :: MenuItem } -- Seitenspezifische Aktion, selten
|
||||||
|
|
||||||
-- | A convenient synonym for creating forms.
|
-- | Convenient Type Synonyms:
|
||||||
|
type DB a = YesodDB UniWorX a
|
||||||
type Form x = Html -> MForm (HandlerT UniWorX IO) (FormResult x, Widget)
|
type Form x = Html -> MForm (HandlerT UniWorX IO) (FormResult x, Widget)
|
||||||
|
|
||||||
mkMessage "UniWorX" "messages" "de"
|
mkMessage "UniWorX" "messages" "de"
|
||||||
@ -151,7 +160,7 @@ instance Yesod UniWorX where
|
|||||||
isAuthorized TermShowR _ = return Authorized
|
isAuthorized TermShowR _ = return Authorized
|
||||||
isAuthorized CourseListR _ = return Authorized
|
isAuthorized CourseListR _ = return Authorized
|
||||||
isAuthorized (CourseListTermR _) _ = return Authorized
|
isAuthorized (CourseListTermR _) _ = return Authorized
|
||||||
isAuthorized (CourseShowR _ _) _ = return Authorized
|
isAuthorized (CourseR _ _ CourseShowR) _ = return Authorized
|
||||||
isAuthorized (CryptoUUIDDispatchR _) _ = return Authorized
|
isAuthorized (CryptoUUIDDispatchR _) _ = return Authorized
|
||||||
isAuthorized SubmissionListR _ = isAuthenticated
|
isAuthorized SubmissionListR _ = isAuthenticated
|
||||||
isAuthorized SubmissionDownloadMultiArchiveR _ = isAuthenticated
|
isAuthorized SubmissionDownloadMultiArchiveR _ = isAuthenticated
|
||||||
@ -198,6 +207,12 @@ instance Yesod UniWorX where
|
|||||||
makeLogger = return . appLogger
|
makeLogger = return . appLogger
|
||||||
|
|
||||||
isAuthorizedDB :: Route UniWorX -> Bool -> YesodDB UniWorX AuthResult
|
isAuthorizedDB :: Route UniWorX -> Bool -> YesodDB UniWorX AuthResult
|
||||||
|
isAuthorizedDB route@(routeAttrs -> attrs) writeable
|
||||||
|
| "adminAny" `member` attrs = adminAccess Nothing
|
||||||
|
| "lecturerAny" `member` attrs = lecturerAccess Nothing
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
isAuthorizedDB UsersR _ = adminAccess Nothing
|
isAuthorizedDB UsersR _ = adminAccess Nothing
|
||||||
isAuthorizedDB (SubmissionR cID) _ = submissionAccess $ Right cID
|
isAuthorizedDB (SubmissionR cID) _ = submissionAccess $ Right cID
|
||||||
isAuthorizedDB (SubmissionDownloadSingleR cID _) _ = submissionAccess $ Right cID
|
isAuthorizedDB (SubmissionDownloadSingleR cID _) _ = submissionAccess $ Right cID
|
||||||
@ -205,14 +220,14 @@ isAuthorizedDB (SubmissionDownloadArchiveR (splitExtension -> (baseName, _))) _
|
|||||||
isAuthorizedDB TermEditR _ = adminAccess Nothing
|
isAuthorizedDB TermEditR _ = adminAccess Nothing
|
||||||
isAuthorizedDB (TermEditExistR _) _ = adminAccess Nothing
|
isAuthorizedDB (TermEditExistR _) _ = adminAccess Nothing
|
||||||
isAuthorizedDB CourseNewR _ = lecturerAccess Nothing
|
isAuthorizedDB CourseNewR _ = lecturerAccess Nothing
|
||||||
isAuthorizedDB (CourseEditR t c) _ = courseLecturerAccess . entityKey =<< getBy404 (CourseTermShort t c)
|
isAuthorizedDB (CourseR t c CourseEditR) _ = courseLecturerAccess . entityKey =<< getBy404 (CourseTermShort t c)
|
||||||
isAuthorizedDB (SheetListR t c) False = return Authorized --
|
isAuthorizedDB (CourseR t c (SheetR SheetListR)) False = return Authorized --
|
||||||
isAuthorizedDB (SheetShowR t c s) _ = return Authorized -- TODO: nur für angemeldete Kursteilnehmer falls sichtbar, sonst nur Lectrurer oder Korrektor
|
isAuthorizedDB (CourseR t c (SheetR SheetListR)) _ = courseLecturerAccess . entityKey =<< getBy404 (CourseTermShort t c)
|
||||||
isAuthorizedDB (SheetFileR t c s _ _) _ = return Authorized -- TODO: nur für angemeldete Kursteilnehmer falls sichtbar, sonst nur Lectrurer oder Korrektor
|
isAuthorizedDB (CourseR t c (SheetR (SheetShowR s))) _ = return Authorized -- TODO: nur für angemeldete Kursteilnehmer falls sichtbar, sonst nur Lectrurer oder Korrektor
|
||||||
isAuthorizedDB (SheetListR t c) _ = courseLecturerAccess . entityKey =<< getBy404 (CourseTermShort t c)
|
isAuthorizedDB (CourseR t c (SheetR (SheetFileR s _ _))) _ = return Authorized -- TODO: nur für angemeldete Kursteilnehmer falls sichtbar, sonst nur Lectrurer oder Korrektor
|
||||||
isAuthorizedDB (SheetNewR t c) _ = courseLecturerAccess . entityKey =<< getBy404 (CourseTermShort t c)
|
isAuthorizedDB (CourseR t c (SheetR SheetNewR)) _ = courseLecturerAccess . entityKey =<< getBy404 (CourseTermShort t c)
|
||||||
isAuthorizedDB (SheetEditR t c s) _ = courseLecturerAccess . entityKey =<< getBy404 (CourseTermShort t c)
|
isAuthorizedDB (CourseR t c (SheetR (SheetEditR s))) _ = courseLecturerAccess . entityKey =<< getBy404 (CourseTermShort t c)
|
||||||
isAuthorizedDB (SheetDelR t c s) _ = courseLecturerAccess . entityKey =<< getBy404 (CourseTermShort t c)
|
isAuthorizedDB (CourseR t c (SheetR (SheetDelR s))) _ = courseLecturerAccess . entityKey =<< getBy404 (CourseTermShort t c)
|
||||||
isAuthorizedDB (CourseEditIDR cID) _ = do
|
isAuthorizedDB (CourseEditIDR cID) _ = do
|
||||||
courseId <- decrypt cID
|
courseId <- decrypt cID
|
||||||
courseLecturerAccess courseId
|
courseLecturerAccess courseId
|
||||||
@ -285,15 +300,15 @@ instance YesodBreadcrumbs UniWorX where
|
|||||||
|
|
||||||
breadcrumb CourseListR = return ("Kurs", Just HomeR)
|
breadcrumb CourseListR = return ("Kurs", Just HomeR)
|
||||||
breadcrumb (CourseListTermR term) = return (toPathPiece term, Just TermShowR)
|
breadcrumb (CourseListTermR term) = return (toPathPiece term, Just TermShowR)
|
||||||
breadcrumb (CourseShowR term course) = return (course, Just $ CourseListTermR term)
|
breadcrumb (CourseR term course CourseShowR) = return (course, Just $ CourseListTermR term)
|
||||||
breadcrumb CourseNewR = return ("Neu", Just CourseListR)
|
breadcrumb CourseNewR = return ("Neu", Just CourseListR)
|
||||||
breadcrumb (CourseEditR _ _) = return ("Editieren", Just CourseListR)
|
breadcrumb (CourseR _ _ CourseEditR) = return ("Editieren", Just CourseListR)
|
||||||
|
|
||||||
breadcrumb (SheetListR tid csh) = return ("Übungen",Just $ CourseShowR tid csh)
|
breadcrumb (CourseR tid csh (SheetR SheetListR)) = return ("Übungen",Just $ CourseR tid csh CourseShowR)
|
||||||
breadcrumb (SheetNewR tid csh) = return ("Neu", Just $ SheetListR tid csh)
|
breadcrumb (CourseR tid csh (SheetR SheetNewR )) = return ("Neu", Just $ CourseR tid csh $ SheetR SheetListR)
|
||||||
breadcrumb (SheetShowR tid csh shn) = return (shn, Just $ SheetListR tid csh)
|
breadcrumb (CourseR tid csh (SheetR (SheetShowR shn))) = return (shn, Just $ CourseR tid csh $ SheetR SheetListR)
|
||||||
breadcrumb (SheetEditR tid csh shn) = return ("Edit", Just $ SheetShowR tid csh shn)
|
breadcrumb (CourseR tid csh (SheetR (SheetEditR shn))) = return ("Edit", Just $ CourseR tid csh $ SheetR $ SheetShowR shn)
|
||||||
breadcrumb (SheetDelR tid csh shn) = return ("DELETE", Just $ SheetShowR tid csh shn)
|
breadcrumb (CourseR tid csh (SheetR (SheetDelR shn))) = return ("DELETE", Just $ CourseR tid csh $ SheetR $ SheetShowR shn)
|
||||||
|
|
||||||
breadcrumb SubmissionListR = return ("Abgaben", Just HomeR)
|
breadcrumb SubmissionListR = return ("Abgaben", Just HomeR)
|
||||||
breadcrumb (SubmissionR _) = return ("Abgabe", Just SubmissionListR)
|
breadcrumb (SubmissionR _) = return ("Abgabe", Just SubmissionListR)
|
||||||
@ -384,6 +399,14 @@ defaultMenuLayout menu widget = do
|
|||||||
asidenav = $(widgetFile "widgets/asidenav")
|
asidenav = $(widgetFile "widgets/asidenav")
|
||||||
breadcrumbs :: Widget
|
breadcrumbs :: Widget
|
||||||
breadcrumbs = $(widgetFile "widgets/breadcrumbs")
|
breadcrumbs = $(widgetFile "widgets/breadcrumbs")
|
||||||
|
pageactionprime :: Widget
|
||||||
|
pageactionprime = $(widgetFile "widgets/pageactionprime")
|
||||||
|
-- functions to determine if there are page-actions
|
||||||
|
isPageActionPrime :: MenuTypes -> Bool
|
||||||
|
isPageActionPrime (PageActionPrime _) = True
|
||||||
|
isPageActionPrime _ = False
|
||||||
|
hasPageActions :: Bool
|
||||||
|
hasPageActions = any isPageActionPrime menuTypes
|
||||||
|
|
||||||
pc <- widgetToPageContent $ do
|
pc <- widgetToPageContent $ do
|
||||||
addStylesheetRemote "https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,800,900"
|
addStylesheetRemote "https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,800,900"
|
||||||
@ -391,6 +414,7 @@ defaultMenuLayout menu widget = do
|
|||||||
addStylesheet $ StaticR css_fonts_css
|
addStylesheet $ StaticR css_fonts_css
|
||||||
addStylesheet $ StaticR css_icons_css
|
addStylesheet $ StaticR css_icons_css
|
||||||
$(widgetFile "default-layout")
|
$(widgetFile "default-layout")
|
||||||
|
$(widgetFile "standalone/modal")
|
||||||
$(widgetFile "standalone/showHide")
|
$(widgetFile "standalone/showHide")
|
||||||
$(widgetFile "standalone/sortable")
|
$(widgetFile "standalone/sortable")
|
||||||
$(widgetFile "standalone/inputs")
|
$(widgetFile "standalone/inputs")
|
||||||
|
|||||||
@ -40,7 +40,7 @@ getCourseListTermR tidini = do
|
|||||||
let c = entityVal ckv
|
let c = entityVal ckv
|
||||||
shd = courseShorthand c
|
shd = courseShorthand c
|
||||||
tid = courseTermId c
|
tid = courseTermId c
|
||||||
in [whamlet| <a href=@{CourseShowR tid shd}>#{shd} |] )
|
in [whamlet| <a href=@{CourseR tid shd CourseShowR}>#{shd} |] )
|
||||||
-- , headed "Institut" $ [shamlet| #{course} |]
|
-- , headed "Institut" $ [shamlet| #{course} |]
|
||||||
, headed "Beginn Anmeldung" $ fromString.(maybe "" formatTimeGerWD).courseRegisterFrom.entityVal
|
, headed "Beginn Anmeldung" $ fromString.(maybe "" formatTimeGerWD).courseRegisterFrom.entityVal
|
||||||
, headed "Ende Anmeldung" $ fromString.(maybe "" formatTimeGerWD).courseRegisterTo.entityVal
|
, headed "Ende Anmeldung" $ fromString.(maybe "" formatTimeGerWD).courseRegisterTo.entityVal
|
||||||
@ -54,17 +54,17 @@ getCourseListTermR tidini = do
|
|||||||
shd = courseShorthand c
|
shd = courseShorthand c
|
||||||
tid = courseTermId c
|
tid = courseTermId c
|
||||||
in do
|
in do
|
||||||
adminLink <- handlerToWidget $ isAuthorized (CourseEditR tid shd ) False
|
adminLink <- handlerToWidget $ isAuthorized (CourseR tid shd CourseEditR) False
|
||||||
-- if (adminLink==Authorized) then linkButton "Ändern" BCWarning (CourseEditR tid shd) else ""
|
-- if (adminLink==Authorized) then linkButton "Ändern" BCWarning (CourseEditR tid shd) else ""
|
||||||
[whamlet|
|
[whamlet|
|
||||||
$if adminLink == Authorized
|
$if adminLink == Authorized
|
||||||
<a href=@{CourseEditR tid shd}>
|
<a href=@{CourseR tid shd CourseEditR}>
|
||||||
editieren
|
editieren
|
||||||
|]
|
|]
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
let pageLinks =
|
let pageLinks =
|
||||||
[ NavbarAside $ MenuItem
|
[ PageActionPrime $ MenuItem
|
||||||
{ menuItemLabel = "Neuer Kurs"
|
{ menuItemLabel = "Neuer Kurs"
|
||||||
, menuItemIcon = Just "book"
|
, menuItemIcon = Just "book"
|
||||||
, menuItemRoute = CourseNewR
|
, menuItemRoute = CourseNewR
|
||||||
@ -93,11 +93,11 @@ getCourseShowR tid csh = do
|
|||||||
let course = entityVal courseEnt
|
let course = entityVal courseEnt
|
||||||
(regWidget, regEnctype) <- generateFormPost $ identifyForm "registerBtn" $ registerButton $ mbRegistered
|
(regWidget, regEnctype) <- generateFormPost $ identifyForm "registerBtn" $ registerButton $ mbRegistered
|
||||||
let pageActions =
|
let pageActions =
|
||||||
[ NavbarAside $ MenuItem
|
[ PageActionPrime $ MenuItem
|
||||||
{ menuItemLabel = "Übungsblätter"
|
{ menuItemLabel = "Übungsblätter"
|
||||||
, menuItemIcon = Nothing
|
, menuItemIcon = Nothing
|
||||||
, menuItemRoute = SheetListR tid csh
|
, menuItemRoute = CSheetR tid csh SheetListR
|
||||||
, menuItemAccessCallback = (== Authorized) <$> isAuthorized (SheetListR tid csh) False
|
, menuItemAccessCallback = (== Authorized) <$> isAuthorized (CSheetR tid csh SheetListR) False
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
defaultLinkLayout pageActions $ do
|
defaultLinkLayout pageActions $ do
|
||||||
@ -145,6 +145,9 @@ getCourseEditR tid csh = do
|
|||||||
course <- runDB $ getBy $ CourseTermShort tid csh
|
course <- runDB $ getBy $ CourseTermShort tid csh
|
||||||
courseEditHandler course
|
courseEditHandler course
|
||||||
|
|
||||||
|
postCourseEditR :: TermId -> Text -> Handler Html
|
||||||
|
postCourseEditR = getCourseEditR
|
||||||
|
|
||||||
getCourseEditIDR :: CryptoUUIDCourse -> Handler Html
|
getCourseEditIDR :: CryptoUUIDCourse -> Handler Html
|
||||||
getCourseEditIDR cID = do
|
getCourseEditIDR cID = do
|
||||||
cIDKey <- getsYesod appCryptoIDKey
|
cIDKey <- getsYesod appCryptoIDKey
|
||||||
@ -174,7 +177,7 @@ courseEditHandler course = do
|
|||||||
, cfTerm = tid
|
, cfTerm = tid
|
||||||
})) -> do -- create new course
|
})) -> do -- create new course
|
||||||
let tident = unTermKey tid
|
let tident = unTermKey tid
|
||||||
actTime <- liftIO getCurrentTime
|
now <- liftIO getCurrentTime
|
||||||
insertOkay <- runDB $ insertUnique $ Course
|
insertOkay <- runDB $ insertUnique $ Course
|
||||||
{ courseName = cfName res
|
{ courseName = cfName res
|
||||||
, courseDescription = cfDesc res
|
, courseDescription = cfDesc res
|
||||||
@ -186,14 +189,12 @@ courseEditHandler course = do
|
|||||||
, courseHasRegistration = cfHasReg res
|
, courseHasRegistration = cfHasReg res
|
||||||
, courseRegisterFrom = cfRegFrom res
|
, courseRegisterFrom = cfRegFrom res
|
||||||
, courseRegisterTo = cfRegTo res
|
, courseRegisterTo = cfRegTo res
|
||||||
, courseCreated = actTime
|
}
|
||||||
, courseChanged = actTime
|
|
||||||
, courseCreatedBy = aid
|
|
||||||
, courseChangedBy = aid
|
|
||||||
}
|
|
||||||
case insertOkay of
|
case insertOkay of
|
||||||
(Just cid) -> do
|
(Just cid) -> do
|
||||||
runDB $ insert_ $ Lecturer aid cid
|
runDB $ do
|
||||||
|
insert_ $ CourseEdit aid now cid
|
||||||
|
insert_ $ Lecturer aid cid
|
||||||
addMessageI "info" $ MsgCourseNewOk tident csh
|
addMessageI "info" $ MsgCourseNewOk tident csh
|
||||||
redirect $ CourseListTermR tid
|
redirect $ CourseListTermR tid
|
||||||
Nothing ->
|
Nothing ->
|
||||||
@ -205,7 +206,7 @@ courseEditHandler course = do
|
|||||||
, cfTerm = tid
|
, cfTerm = tid
|
||||||
})) -> do -- edit existing course
|
})) -> do -- edit existing course
|
||||||
let tident = unTermKey tid
|
let tident = unTermKey tid
|
||||||
actTime <- liftIO getCurrentTime
|
now <- liftIO getCurrentTime
|
||||||
-- addMessage "debug" [shamlet| #{show res}|]
|
-- addMessage "debug" [shamlet| #{show res}|]
|
||||||
runDB $ do
|
runDB $ do
|
||||||
old <- get cid
|
old <- get cid
|
||||||
@ -228,9 +229,9 @@ courseEditHandler course = do
|
|||||||
-- , CourseRegisterFrom =. cfRegFrom res
|
-- , CourseRegisterFrom =. cfRegFrom res
|
||||||
-- , CourseRegisterTo =. cfRegTo res
|
-- , CourseRegisterTo =. cfRegTo res
|
||||||
-- , CourseChangedBy =. aid
|
-- , CourseChangedBy =. aid
|
||||||
-- , CourseChanged =. actTime
|
-- , CourseChanged =. now
|
||||||
-- ]
|
-- ]
|
||||||
updOkay <- replace cid ( -- TODO replaceUnique requires Eq?!
|
_updOkay <- replace cid ( -- TODO replaceUnique requires Eq?!
|
||||||
Course { courseName = cfName res
|
Course { courseName = cfName res
|
||||||
, courseDescription = cfDesc res
|
, courseDescription = cfDesc res
|
||||||
, courseLinkExternal = cfLink res
|
, courseLinkExternal = cfLink res
|
||||||
@ -238,15 +239,12 @@ courseEditHandler course = do
|
|||||||
, courseTermId = cfTerm res
|
, courseTermId = cfTerm res
|
||||||
, courseSchoolId = cfSchool res
|
, courseSchoolId = cfSchool res
|
||||||
, courseCapacity = cfCapacity res
|
, courseCapacity = cfCapacity res
|
||||||
, courseChanged = actTime
|
|
||||||
, courseChangedBy = aid
|
|
||||||
, courseCreated = courseCreated oldCourse
|
|
||||||
, courseCreatedBy = courseCreatedBy oldCourse
|
|
||||||
, courseHasRegistration = cfHasReg res
|
, courseHasRegistration = cfHasReg res
|
||||||
, courseRegisterFrom = cfRegFrom res
|
, courseRegisterFrom = cfRegFrom res
|
||||||
, courseRegisterTo = cfRegTo res
|
, courseRegisterTo = cfRegTo res
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
insert_ $ CourseEdit aid now cid
|
||||||
-- if (isNothing updOkay)
|
-- if (isNothing updOkay)
|
||||||
-- then do
|
-- then do
|
||||||
addMessageI "info" $ MsgCourseEditOk tident csh
|
addMessageI "info" $ MsgCourseEditOk tident csh
|
||||||
@ -256,7 +254,7 @@ courseEditHandler course = do
|
|||||||
(FormFailure _) -> addMessageI "warning" MsgInvalidInput
|
(FormFailure _) -> addMessageI "warning" MsgInvalidInput
|
||||||
other -> addMessage "error" $ [shamlet| Error: #{show other}|]
|
other -> addMessage "error" $ [shamlet| Error: #{show other}|]
|
||||||
let formTitle = "Kurs editieren/anlegen" :: Text
|
let formTitle = "Kurs editieren/anlegen" :: Text
|
||||||
let actionUrl = CourseNewR -- CourseEditR -- TODO
|
actionUrl <- fromMaybe CourseNewR <$> getCurrentRoute
|
||||||
defaultLayout $ do
|
defaultLayout $ do
|
||||||
setTitle [shamlet| #{formTitle} |]
|
setTitle [shamlet| #{formTitle} |]
|
||||||
$(widgetFile "formPage")
|
$(widgetFile "formPage")
|
||||||
|
|||||||
@ -172,30 +172,30 @@ getSheetList courseEnt = do
|
|||||||
rated <- count $ (SubmissionRatingTime !=. Nothing):sheetsub
|
rated <- count $ (SubmissionRatingTime !=. Nothing):sheetsub
|
||||||
return (sid, sheet, (submissions, rated))
|
return (sid, sheet, (submissions, rated))
|
||||||
let colBase = mconcat
|
let colBase = mconcat
|
||||||
[ headed "Blatt" $ \(sid,sheet,_) -> linkButton (toWgt $ sheetName sheet) BCLink $ SheetShowR tid csh (sheetName sheet)
|
[ headed "Blatt" $ \(sid,sheet,_) -> linkButton (toWgt $ sheetName sheet) BCLink $ CourseR tid csh $ SheetR $ SheetShowR $ sheetName sheet
|
||||||
, headed "Abgabe ab" $ toWgt . formatTimeGerWD . sheetActiveFrom . snd3
|
, headed "Abgabe ab" $ toWgt . formatTimeGerWD . sheetActiveFrom . snd3
|
||||||
, headed "Abgabe bis" $ toWgt . formatTimeGerWD . sheetActiveTo . snd3
|
, headed "Abgabe bis" $ toWgt . formatTimeGerWD . sheetActiveTo . snd3
|
||||||
, headed "Bewertung" $ toWgt . show . sheetType . snd3
|
, headed "Bewertung" $ toWgt . show . sheetType . snd3
|
||||||
, headed "Korrigiert" $ toWgt . snd . trd3
|
|
||||||
, headed "Eingereicht" $ toWgt . fst . trd3
|
|
||||||
]
|
]
|
||||||
let colAdmin = mconcat -- only show edit button for allowed course assistants
|
let colAdmin = mconcat -- only show edit button for allowed course assistants
|
||||||
[ headed "" $ \s -> linkButton "Edit" BCLink $ SheetEditR tid csh $ sheetName $ snd3 s
|
[ headed "Korrigiert" $ toWgt . snd . trd3
|
||||||
, headed "" $ \s -> linkButton "Delete" BCLink $ SheetDelR tid csh $ sheetName $ snd3 s
|
, headed "Eingereicht" $ toWgt . fst . trd3
|
||||||
|
, headed "" $ \s -> linkButton "Edit" BCLink $ CourseR tid csh $ SheetR $ SheetEditR $ sheetName $ snd3 s
|
||||||
|
, headed "" $ \s -> linkButton "Delete" BCLink $ CourseR tid csh $ SheetR $ SheetDelR $ sheetName $ snd3 s
|
||||||
]
|
]
|
||||||
showAdmin <- case sheets of
|
showAdmin <- case sheets of
|
||||||
((_,firstSheet,_):_) -> do
|
((_,firstSheet,_):_) -> do
|
||||||
setUltDestCurrent
|
setUltDestCurrent
|
||||||
(Authorized ==) <$> isAuthorized (SheetEditR tid csh $ sheetName firstSheet) False
|
(Authorized ==) <$> isAuthorized (CourseR tid csh $ SheetR $ SheetEditR $ sheetName firstSheet) False
|
||||||
_otherwise -> return False
|
_otherwise -> return False
|
||||||
let colSheets = if showAdmin
|
let colSheets = if showAdmin
|
||||||
then colBase `mappend` colAdmin
|
then colBase `mappend` colAdmin
|
||||||
else colBase
|
else colBase
|
||||||
let pageActions =
|
let pageActions =
|
||||||
[ NavbarAside $ MenuItem
|
[ PageActionPrime $ MenuItem
|
||||||
{ menuItemLabel = "Neues Übungsblatt"
|
{ menuItemLabel = "Neues Übungsblatt"
|
||||||
, menuItemIcon = Nothing
|
, menuItemIcon = Nothing
|
||||||
, menuItemRoute = SheetNewR tid csh
|
, menuItemRoute = CSheetR tid csh SheetNewR
|
||||||
, menuItemAccessCallback = (== Authorized) <$> isAuthorized CourseNewR False
|
, menuItemAccessCallback = (== Authorized) <$> isAuthorized CourseNewR False
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -221,7 +221,7 @@ getSheetShowR tid csh shn = do
|
|||||||
E.where_ (sheet E.^. SheetId E.==. E.val sid )
|
E.where_ (sheet E.^. SheetId E.==. E.val sid )
|
||||||
-- return desired columns
|
-- return desired columns
|
||||||
return $ (file E.^. FileTitle, sheetFile E.^. SheetFileType)
|
return $ (file E.^. FileTitle, sheetFile E.^. SheetFileType)
|
||||||
let fileLinks = map (\(E.Value fName, E.Value fType) -> SheetFileR tid csh shn fType fName) fileNameTypes
|
let fileLinks = map (\(E.Value fName, E.Value fType) -> CSheetR tid csh (SheetFileR shn fType fName)) fileNameTypes
|
||||||
|
|
||||||
defaultLayout $ do
|
defaultLayout $ do
|
||||||
setTitle $ toHtml $ T.append "Übung " $ sheetName sheet
|
setTitle $ toHtml $ T.append "Übung " $ sheetName sheet
|
||||||
@ -295,8 +295,6 @@ getSheetEditR tid csh shn = do
|
|||||||
}
|
}
|
||||||
let action newSheet = do
|
let action newSheet = do
|
||||||
replaceRes <- myReplaceUnique sid $ newSheet
|
replaceRes <- myReplaceUnique sid $ newSheet
|
||||||
{ sheetCreated = sheetCreated
|
|
||||||
, sheetCreatedBy = sheetChangedBy }
|
|
||||||
case replaceRes of
|
case replaceRes of
|
||||||
Nothing -> return $ Just sid
|
Nothing -> return $ Just sid
|
||||||
(Just _err) -> return $ Nothing -- More specific error message for edit old sheet could go here
|
(Just _err) -> return $ Nothing -- More specific error message for edit old sheet could go here
|
||||||
@ -314,8 +312,8 @@ handleSheetEdit tid csh msId template dbAction = do
|
|||||||
case res of
|
case res of
|
||||||
(FormSuccess SheetForm{..}) -> do
|
(FormSuccess SheetForm{..}) -> do
|
||||||
saveOkay <- runDB $ do
|
saveOkay <- runDB $ do
|
||||||
cid <- getKeyBy404 $ CourseTermShort tid csh
|
|
||||||
actTime <- liftIO getCurrentTime
|
actTime <- liftIO getCurrentTime
|
||||||
|
cid <- getKeyBy404 $ CourseTermShort tid csh
|
||||||
let newSheet = Sheet
|
let newSheet = Sheet
|
||||||
{ sheetCourseId = cid
|
{ sheetCourseId = cid
|
||||||
, sheetName = sfName
|
, sheetName = sfName
|
||||||
@ -328,10 +326,6 @@ handleSheetEdit tid csh msId template dbAction = do
|
|||||||
, sheetActiveTo = sfActiveTo
|
, sheetActiveTo = sfActiveTo
|
||||||
, sheetHintFrom = sfHintFrom
|
, sheetHintFrom = sfHintFrom
|
||||||
, sheetSolutionFrom = sfSolutionFrom
|
, sheetSolutionFrom = sfSolutionFrom
|
||||||
, sheetCreated = actTime -- dbAction adjusts this for replacement, TODO: eigene Tabelle für changedBy
|
|
||||||
, sheetChanged = actTime
|
|
||||||
, sheetCreatedBy = aid -- dbAction adjusts this for replacement
|
|
||||||
, sheetChangedBy = aid
|
|
||||||
}
|
}
|
||||||
mbsid <- dbAction newSheet
|
mbsid <- dbAction newSheet
|
||||||
case mbsid of
|
case mbsid of
|
||||||
@ -340,16 +334,17 @@ handleSheetEdit tid csh msId template dbAction = do
|
|||||||
whenIsJust sfSheetF $ insertSheetFile' sid SheetExercise
|
whenIsJust sfSheetF $ insertSheetFile' sid SheetExercise
|
||||||
whenIsJust sfHintF $ insertSheetFile sid SheetHint
|
whenIsJust sfHintF $ insertSheetFile sid SheetHint
|
||||||
whenIsJust sfSolutionF $ insertSheetFile sid SheetSolution
|
whenIsJust sfSolutionF $ insertSheetFile sid SheetSolution
|
||||||
|
insert_ $ SheetEdit aid actTime sid
|
||||||
addMessageI "info" $ MsgSheetEditOk tident csh sfName
|
addMessageI "info" $ MsgSheetEditOk tident csh sfName
|
||||||
return True
|
return True
|
||||||
when saveOkay $ redirect $ SheetShowR tid csh sfName -- redirect must happen outside of runDB
|
when saveOkay $ redirect $ CSheetR tid csh $ SheetShowR sfName -- redirect must happen outside of runDB
|
||||||
(FormFailure msgs) -> forM_ msgs $ (addMessage "warning") . toHtml
|
(FormFailure msgs) -> forM_ msgs $ (addMessage "warning") . toHtml
|
||||||
_ -> return ()
|
_ -> return ()
|
||||||
let pageTitle = maybe (MsgSheetTitleNew tident csh)
|
let pageTitle = maybe (MsgSheetTitleNew tident csh)
|
||||||
(MsgSheetTitle tident csh) mbshn
|
(MsgSheetTitle tident csh) mbshn
|
||||||
let formTitle = pageTitle
|
let formTitle = pageTitle
|
||||||
let formText = Nothing :: Maybe UniWorXMessage
|
let formText = Nothing :: Maybe UniWorXMessage
|
||||||
actionUrl <- fromMaybe (SheetNewR tid csh) <$> getCurrentRoute
|
actionUrl <- fromMaybe (CSheetR tid csh SheetNewR) <$> getCurrentRoute
|
||||||
defaultLayout $ do
|
defaultLayout $ do
|
||||||
setTitleI pageTitle
|
setTitleI pageTitle
|
||||||
$(widgetFile "formPageI18n")
|
$(widgetFile "formPageI18n")
|
||||||
@ -361,19 +356,19 @@ getSheetDelR tid csh shn = do
|
|||||||
let tident = unTermKey tid
|
let tident = unTermKey tid
|
||||||
((result,formWidget), formEnctype) <- runFormPost (buttonForm :: Form BtnDelete)
|
((result,formWidget), formEnctype) <- runFormPost (buttonForm :: Form BtnDelete)
|
||||||
case result of
|
case result of
|
||||||
(FormSuccess BtnAbort) -> redirectUltDest $ SheetShowR tid csh shn
|
(FormSuccess BtnAbort) -> redirectUltDest $ CSheetR tid csh $ SheetShowR shn
|
||||||
(FormSuccess BtnDelete) -> do
|
(FormSuccess BtnDelete) -> do
|
||||||
runDB $ fetchSheetId tid csh shn >>= deleteCascade
|
runDB $ fetchSheetId tid csh shn >>= deleteCascade
|
||||||
-- TODO: deleteCascade löscht aber nicht die hochgeladenen Dateien!!!
|
-- TODO: deleteCascade löscht aber nicht die hochgeladenen Dateien!!!
|
||||||
setMessageI $ MsgSheetDelOk tident csh shn
|
setMessageI $ MsgSheetDelOk tident csh shn
|
||||||
redirect $ SheetListR tid csh
|
redirect $ CSheetR tid csh SheetListR
|
||||||
_other -> do
|
_other -> do
|
||||||
submissionno <- runDB $ do
|
submissionno <- runDB $ do
|
||||||
sid <- fetchSheetId tid csh shn
|
sid <- fetchSheetId tid csh shn
|
||||||
count [SubmissionSheetId ==. sid]
|
count [SubmissionSheetId ==. sid]
|
||||||
let formTitle = MsgSheetDelTitle tident csh shn
|
let formTitle = MsgSheetDelTitle tident csh shn
|
||||||
let formText = Just $ MsgSheetDelText submissionno
|
let formText = Just $ MsgSheetDelText submissionno
|
||||||
let actionUrl = SheetDelR tid csh shn
|
let actionUrl = CSheetR tid csh $ SheetDelR shn
|
||||||
defaultLayout $ do
|
defaultLayout $ do
|
||||||
setTitleI $ MsgSheetTitle tident csh shn
|
setTitleI $ MsgSheetTitle tident csh shn
|
||||||
$(widgetFile "formPageI18n")
|
$(widgetFile "formPageI18n")
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
{-# LANGUAGE FlexibleContexts #-}
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
{-# LANGUAGE TupleSections #-}
|
{-# LANGUAGE TupleSections #-}
|
||||||
{-# LANGUAGE LambdaCase #-}
|
{-# LANGUAGE LambdaCase #-}
|
||||||
|
{-# LANGUAGE ViewPatterns #-}
|
||||||
|
|
||||||
module Handler.Submission where
|
module Handler.Submission where
|
||||||
|
|
||||||
@ -53,7 +54,7 @@ submissionTable = do
|
|||||||
(,,) <$> encrypt submissionId <*> encrypt submissionId <*> pure s
|
(,,) <$> encrypt submissionId <*> encrypt submissionId <*> pure s
|
||||||
|
|
||||||
let
|
let
|
||||||
anchorCourse (_, _, (_, _, Entity _ Course{..})) = CourseShowR courseTermId courseShorthand
|
anchorCourse (_, _, (_, _, Entity _ Course{..})) = CourseR courseTermId courseShorthand CourseShowR
|
||||||
courseText (_, _, (_, _, Entity _ Course{..})) = toWidget courseName
|
courseText (_, _, (_, _, Entity _ Course{..})) = toWidget courseName
|
||||||
anchorSubmission (_, cUUID, _) = SubmissionR cUUID
|
anchorSubmission (_, cUUID, _) = SubmissionR cUUID
|
||||||
submissionText (cID, _, _) = toWidget . toPathPiece . CI.foldedCase $ ciphertext cID
|
submissionText (cID, _, _) = toWidget . toPathPiece . CI.foldedCase $ ciphertext cID
|
||||||
@ -177,8 +178,12 @@ postSubmissionDownloadMultiArchiveR = do
|
|||||||
|
|
||||||
withinDirectory f@File{..} = f { fileTitle = directoryName </> fileTitle }
|
withinDirectory f@File{..} = f { fileTitle = directoryName </> fileTitle }
|
||||||
|
|
||||||
|
lastEditMb <- lift $ selectList [SubmissionEditSubmission ==. submissionID] [Desc SubmissionEditTime, LimitTo 1]
|
||||||
|
lastEditTime <- case lastEditMb of
|
||||||
|
[(submissionEditTime.entityVal -> time)] -> return time
|
||||||
|
_other -> liftIO getCurrentTime
|
||||||
yield $ File
|
yield $ File
|
||||||
{ fileModified = submissionChanged
|
{ fileModified = lastEditTime
|
||||||
, fileTitle = directoryName
|
, fileTitle = directoryName
|
||||||
, fileContent = Nothing
|
, fileContent = Nothing
|
||||||
}
|
}
|
||||||
|
|||||||
@ -76,7 +76,7 @@ getTermShowR = do
|
|||||||
, dbtIdent = "terms" :: Text
|
, dbtIdent = "terms" :: Text
|
||||||
}
|
}
|
||||||
let pageActions =
|
let pageActions =
|
||||||
[ NavbarAside $ MenuItem
|
[ PageActionPrime $ MenuItem
|
||||||
{ menuItemLabel = "Neues Semester"
|
{ menuItemLabel = "Neues Semester"
|
||||||
, menuItemIcon = Nothing
|
, menuItemIcon = Nothing
|
||||||
, menuItemRoute = TermEditR
|
, menuItemRoute = TermEditR
|
||||||
|
|||||||
@ -70,12 +70,9 @@ sinkSubmission sheetId userId mExists = do
|
|||||||
submissionRatingComment = Nothing
|
submissionRatingComment = Nothing
|
||||||
submissionRatingBy = Nothing
|
submissionRatingBy = Nothing
|
||||||
submissionRatingTime = Nothing
|
submissionRatingTime = Nothing
|
||||||
submissionCreated = now
|
|
||||||
submissionChanged = now
|
|
||||||
submissionCreatedBy = userId
|
|
||||||
submissionChangedBy = userId
|
|
||||||
|
|
||||||
(sId, isUpdate) <- lift $ maybe ((, False) <$> insert Submission{..}) return mExists
|
(sId, isUpdate) <- lift $ maybe ((, False) <$> (insert Submission{..} >>= (\sid -> sid <$ insert (SubmissionEdit userId now sid)))) return mExists
|
||||||
|
|
||||||
|
|
||||||
sId <$ sinkSubmission' sId isUpdate
|
sId <$ sinkSubmission' sId isUpdate
|
||||||
where
|
where
|
||||||
@ -184,9 +181,9 @@ sinkSubmission sheetId userId mExists = do
|
|||||||
alreadyTouched <- gets $ getAny . sinkSubmissionTouched
|
alreadyTouched <- gets $ getAny . sinkSubmissionTouched
|
||||||
when (not alreadyTouched) $ do
|
when (not alreadyTouched) $ do
|
||||||
now <- liftIO getCurrentTime
|
now <- liftIO getCurrentTime
|
||||||
lift . update submissionId $ case isUpdate of
|
lift $ case isUpdate of
|
||||||
False -> [ SubmissionChangedBy =. userId, SubmissionChanged =. now ]
|
False -> insert_ $ SubmissionEdit userId now submissionId
|
||||||
True -> [ SubmissionRatingBy =. Just userId, SubmissionRatingTime =. Just now ]
|
True -> update submissionId [ SubmissionRatingBy =. Just userId, SubmissionRatingTime =. Just now ]
|
||||||
tell $ mempty{ sinkSubmissionTouched = Any True }
|
tell $ mempty{ sinkSubmissionTouched = Any True }
|
||||||
|
|
||||||
finalize :: SubmissionSinkState -> YesodDB UniWorX ()
|
finalize :: SubmissionSinkState -> YesodDB UniWorX ()
|
||||||
|
|||||||
@ -6,3 +6,17 @@ import Import.NoFoundation
|
|||||||
|
|
||||||
lipsum :: WidgetT site IO ()
|
lipsum :: WidgetT site IO ()
|
||||||
lipsum = $(widgetFile "widgets/lipsum")
|
lipsum = $(widgetFile "widgets/lipsum")
|
||||||
|
|
||||||
|
modal :: [Char] -> Maybe [Char] -> WidgetT site IO ()
|
||||||
|
modal modalTrigger (Just modalContent) = do
|
||||||
|
let
|
||||||
|
modalId :: Int32
|
||||||
|
modalId = 13
|
||||||
|
$(widgetFile "widgets/modal")
|
||||||
|
modal modalTrigger Nothing = do
|
||||||
|
let
|
||||||
|
modalId :: Int32
|
||||||
|
modalId = 13
|
||||||
|
modalContent :: [Char]
|
||||||
|
modalContent = "placeholder"
|
||||||
|
$(widgetFile "widgets/modal")
|
||||||
|
|||||||
@ -55,7 +55,7 @@ deriveJSON defaultOptions ''SheetType
|
|||||||
derivePersistFieldJSON "SheetType"
|
derivePersistFieldJSON "SheetType"
|
||||||
|
|
||||||
data SheetGroup
|
data SheetGroup
|
||||||
= Arbitrary { maxParticipants :: Int }
|
= Arbitrary { maxParticipants :: Int } -- Distinguish Limited/Arbitrary
|
||||||
| RegisteredGroups
|
| RegisteredGroups
|
||||||
| NoGroups
|
| NoGroups
|
||||||
deriving (Show, Read, Eq)
|
deriving (Show, Read, Eq)
|
||||||
|
|||||||
@ -16,7 +16,7 @@
|
|||||||
\ bis #{formatTimeGerWD regTo}
|
\ bis #{formatTimeGerWD regTo}
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<form method=post action=@{CourseShowR tid csh} enctype=#{regEnctype}>
|
<form method=post action=@{CourseR tid csh CourseShowR} enctype=#{regEnctype}>
|
||||||
^{regWidget}
|
^{regWidget}
|
||||||
|
|
||||||
<div .course-header__title>
|
<div .course-header__title>
|
||||||
|
|||||||
@ -38,7 +38,12 @@ $newline never
|
|||||||
\ });
|
\ });
|
||||||
}
|
}
|
||||||
|
|
||||||
<body>
|
|
||||||
|
<body .no-js>
|
||||||
|
<!-- removes no-js class from body if client supports javascript -->
|
||||||
|
<script>
|
||||||
|
document.body.classList.remove('no-js');
|
||||||
|
|
||||||
^{pageBody pc}
|
^{pageBody pc}
|
||||||
|
|
||||||
$maybe analytics <- appAnalytics $ appSettings master
|
$maybe analytics <- appAnalytics $ appSettings master
|
||||||
|
|||||||
@ -13,5 +13,8 @@
|
|||||||
$with status2 <- bool status "info" (status == "")
|
$with status2 <- bool status "info" (status == "")
|
||||||
<div class="alert alert-#{status2}">#{msg}
|
<div class="alert alert-#{status2}">#{msg}
|
||||||
|
|
||||||
|
<!-- prime page actions -->
|
||||||
|
^{pageactionprime}
|
||||||
|
|
||||||
<!-- actual content -->
|
<!-- actual content -->
|
||||||
^{widget}
|
^{widget}
|
||||||
|
|||||||
@ -25,6 +25,7 @@
|
|||||||
--lighterbase: #5F98C2;
|
--lighterbase: #5F98C2;
|
||||||
--whitebase: #FCFFFA;
|
--whitebase: #FCFFFA;
|
||||||
--greybase: #B1B5C0;
|
--greybase: #B1B5C0;
|
||||||
|
--lightgreybase: #D9DEDB;
|
||||||
--blackbase: #1A2A36;
|
--blackbase: #1A2A36;
|
||||||
--fontbase: #34303a;
|
--fontbase: #34303a;
|
||||||
--fontsec: #5b5861;
|
--fontsec: #5b5861;
|
||||||
@ -158,7 +159,7 @@ th {
|
|||||||
input[type="submit"],
|
input[type="submit"],
|
||||||
input[type="button"],
|
input[type="button"],
|
||||||
button,
|
button,
|
||||||
.btn {
|
.btn, a.btn {
|
||||||
outline: 0;
|
outline: 0;
|
||||||
border: 0;
|
border: 0;
|
||||||
box-shadow: 0;
|
box-shadow: 0;
|
||||||
@ -174,12 +175,14 @@ button,
|
|||||||
}
|
}
|
||||||
input.btn-primary,
|
input.btn-primary,
|
||||||
button.btn-primary,
|
button.btn-primary,
|
||||||
|
a.btn.btn-primary,
|
||||||
.btn.btn-primary {
|
.btn.btn-primary {
|
||||||
background-color: var(--primarybase);
|
background-color: var(--primarybase);
|
||||||
}
|
}
|
||||||
|
|
||||||
input.btn-info,
|
input.btn-info,
|
||||||
button.btn-info,
|
button.btn-info,
|
||||||
|
a.btn.btn-info,
|
||||||
.btn.btn-info {
|
.btn.btn-info {
|
||||||
background-color: var(--infobase)
|
background-color: var(--infobase)
|
||||||
}
|
}
|
||||||
@ -187,6 +190,7 @@ button.btn-info,
|
|||||||
input[type="submit"][disabled],
|
input[type="submit"][disabled],
|
||||||
input[type="button"][disabled],
|
input[type="button"][disabled],
|
||||||
button[disabled],
|
button[disabled],
|
||||||
|
a.btn[disabled],
|
||||||
.btn[disabled] {
|
.btn[disabled] {
|
||||||
opacity: 0.3;
|
opacity: 0.3;
|
||||||
background-color: var(--greybase);
|
background-color: var(--greybase);
|
||||||
@ -196,14 +200,17 @@ button[disabled],
|
|||||||
input[type="submit"]:not([disabled]):hover,
|
input[type="submit"]:not([disabled]):hover,
|
||||||
input[type="button"]:not([disabled]):hover,
|
input[type="button"]:not([disabled]):hover,
|
||||||
button:not([disabled]):hover,
|
button:not([disabled]):hover,
|
||||||
|
a.btn:not([disabled]):hover,
|
||||||
.btn:not([disabled]):hover {
|
.btn:not([disabled]):hover {
|
||||||
background-color: var(--lighterbase);
|
background-color: var(--lighterbase);
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="submit"].btn-info:hover,
|
input[type="submit"].btn-info:hover,
|
||||||
input[type="button"].btn-info:hover,
|
input[type="button"].btn-info:hover,
|
||||||
button.btn-info:hover,
|
button.btn-info:hover,
|
||||||
|
a.btn.btn-info:hover,
|
||||||
.btn.btn-info:hover {
|
.btn.btn-info:hover {
|
||||||
background-color: var(--greybase)
|
background-color: var(--greybase)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,7 +39,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>0
|
<td>0
|
||||||
<td>14
|
<td>NT2
|
||||||
<td>CON2
|
<td>CON2
|
||||||
<td>3
|
<td>3
|
||||||
<tr>
|
<tr>
|
||||||
@ -57,6 +57,11 @@
|
|||||||
<td>43
|
<td>43
|
||||||
<td>T2C2
|
<td>T2C2
|
||||||
<td>35
|
<td>35
|
||||||
|
<tr>
|
||||||
|
<td>4
|
||||||
|
<td>73
|
||||||
|
<td>CA62
|
||||||
|
<td>7
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
<div .container>
|
<div .container>
|
||||||
@ -67,3 +72,29 @@
|
|||||||
Knopf-Test:
|
Knopf-Test:
|
||||||
<form .form-inline method=post action=@{HomeR} enctype=#{btnEnctype}>
|
<form .form-inline method=post action=@{HomeR} enctype=#{btnEnctype}>
|
||||||
^{btnWdgt}
|
^{btnWdgt}
|
||||||
|
<li><br>
|
||||||
|
Modals:
|
||||||
|
^{modal ".toggler1" Nothing}
|
||||||
|
<a href="/" .btn.toggler1>Klick mich für Ajax-Test
|
||||||
|
<noscript>(Für Modals bitte JS aktivieren)</noscript>
|
||||||
|
^{modal ".toggler2" (Just "Test wegen Modal")}
|
||||||
|
<div .btn.toggler2>Klick mich für Content-Test
|
||||||
|
<noscript>(Für Modals bitte JS aktivieren)</noscript>
|
||||||
|
|
||||||
|
<li><br>
|
||||||
|
Multi-File-Input für bereits hochgeladene Dateien:
|
||||||
|
<form>
|
||||||
|
<div .form-group>
|
||||||
|
<label .form-group__label>Datei(en)
|
||||||
|
$# file 1
|
||||||
|
<div .file-checkbox__container>
|
||||||
|
<label .file-checkbox__label.reactive-label.btn for="f2-1">Datenschutz.txt
|
||||||
|
<div .checkbox>
|
||||||
|
<input .file-checkbox id="f2-1" name="f2" value="Datenschutz.txt" type="checkbox">
|
||||||
|
<label for="f2-1">
|
||||||
|
$# file 2
|
||||||
|
<div .file-checkbox__container>
|
||||||
|
<label .file-checkbox__label.reactive-label.btn for="f2-2">fill-db.hs
|
||||||
|
<div .checkbox>
|
||||||
|
<input .file-checkbox id="f2-2" name="f2" value="fill-db.hs" type="checkbox">
|
||||||
|
<label for="f2-2">
|
||||||
|
|||||||
@ -29,7 +29,7 @@
|
|||||||
$forall fileLink <- fileLinks
|
$forall fileLink <- fileLinks
|
||||||
<li>
|
<li>
|
||||||
$case fileLink
|
$case fileLink
|
||||||
$of SheetFileR _ _ _ typ name
|
$of CourseR _ _ (SheetR (SheetFileR _ typ name))
|
||||||
#{toPathPiece typ}
|
#{toPathPiece typ}
|
||||||
<a href=@{fileLink}>#{name}
|
<a href=@{fileLink}>#{name}
|
||||||
$of other
|
$of other
|
||||||
|
|||||||
@ -24,10 +24,12 @@
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// allows for multiple file uploads with separate inputs
|
||||||
window.utils.reactiveFileUpload = function(input, parent) {
|
window.utils.reactiveFileUpload = function(input, parent) {
|
||||||
var currValidInputCount = 0;
|
var currValidInputCount = 0;
|
||||||
var addMore = false;
|
var addMore = false;
|
||||||
var inputName = input.getAttribute('name');
|
var inputName = input.getAttribute('name');
|
||||||
|
var isMulti = input.getAttribute('multiple') ? true : false;
|
||||||
// FileInput PseudoClass
|
// FileInput PseudoClass
|
||||||
function FileInput(container, input, label, remover) {
|
function FileInput(container, input, label, remover) {
|
||||||
this.container = container;
|
this.container = container;
|
||||||
@ -42,7 +44,7 @@
|
|||||||
this.remove = function() {
|
this.remove = function() {
|
||||||
this.container.remove();
|
this.container.remove();
|
||||||
}
|
}
|
||||||
this.isValid = function() {
|
this.wasValid = function() {
|
||||||
return this.container.classList.contains('file-input__container--valid');
|
return this.container.classList.contains('file-input__container--valid');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -61,7 +63,9 @@
|
|||||||
parent.classList.add('form-group--valid')
|
parent.classList.add('form-group--valid')
|
||||||
}
|
}
|
||||||
submitBtn.removeAttribute('disabled');
|
submitBtn.removeAttribute('disabled');
|
||||||
addNextInput();
|
if (isMulti) {
|
||||||
|
addNextInput();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (parent.classList.contains('form-group')) {
|
if (parent.classList.contains('form-group')) {
|
||||||
parent.classList.remove('form-group--valid')
|
parent.classList.remove('form-group--valid')
|
||||||
@ -78,14 +82,16 @@
|
|||||||
var fileName = filePath[filePath.length - 1];
|
var fileName = filePath[filePath.length - 1];
|
||||||
fileInput.label.innerHTML = fileName;
|
fileInput.label.innerHTML = fileName;
|
||||||
// increase count if this field was empty previously
|
// increase count if this field was empty previously
|
||||||
if (!fileInput.isValid()) {
|
if (!fileInput.wasValid()) {
|
||||||
currValidInputCount++;
|
currValidInputCount++;
|
||||||
}
|
}
|
||||||
fileInput.container.classList.add('file-input__container--valid')
|
fileInput.container.classList.add('file-input__container--valid')
|
||||||
// show next input
|
// show next input
|
||||||
} else {
|
} else {
|
||||||
currValidInputCount--;
|
if (isMulti) {
|
||||||
fileInput.remove();
|
currValidInputCount--;
|
||||||
|
}
|
||||||
|
clearInput(fileInput);
|
||||||
}
|
}
|
||||||
updateForm();
|
updateForm();
|
||||||
});
|
});
|
||||||
@ -95,26 +101,36 @@
|
|||||||
fileInput.input.addEventListener('blur', function() {
|
fileInput.input.addEventListener('blur', function() {
|
||||||
fileInput.container.classList.remove('pseudo-focus');
|
fileInput.container.classList.remove('pseudo-focus');
|
||||||
});
|
});
|
||||||
fileInput.label.addEventListener('click', function() {
|
|
||||||
fileInput.input.click();
|
|
||||||
});
|
|
||||||
fileInput.remover.addEventListener('click', function() {
|
fileInput.remover.addEventListener('click', function() {
|
||||||
if (fileInput.isValid()) {
|
if (fileInput.wasValid()) {
|
||||||
currValidInputCount--;
|
currValidInputCount--;
|
||||||
}
|
}
|
||||||
fileInput.remove();
|
clearInput(fileInput);
|
||||||
updateForm();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clears or removes fileinput based on multi-file or not
|
||||||
|
function clearInput(fileInput) {
|
||||||
|
if (isMulti) {
|
||||||
|
fileInput.remove();
|
||||||
|
} else {
|
||||||
|
fileInput.container.classList.remove('file-input__container--valid')
|
||||||
|
fileInput.label.innerHTML = '';
|
||||||
|
}
|
||||||
|
updateForm();
|
||||||
|
}
|
||||||
// create new wrapped input element with name name
|
// create new wrapped input element with name name
|
||||||
function makeInput(name) {
|
function makeInput(name) {
|
||||||
var cont = document.createElement('div');
|
var cont = document.createElement('div');
|
||||||
var desc = document.createElement('span');
|
var desc = document.createElement('label');
|
||||||
var nextInput = document.createElement('input');
|
var nextInput = document.createElement('input');
|
||||||
var remover = document.createElement('div');
|
var remover = document.createElement('div');
|
||||||
cont.classList.add('file-input__container');
|
cont.classList.add('file-input__container');
|
||||||
desc.classList.add('file-input__label', 'btn');
|
desc.classList.add('file-input__label', 'btn');
|
||||||
|
nextInput.classList.add('js-file-input');
|
||||||
|
desc.setAttribute('for', name + '-' + currValidInputCount);
|
||||||
remover.classList.add('file-input__remover');
|
remover.classList.add('file-input__remover');
|
||||||
|
nextInput.setAttribute('id', name + '-' + currValidInputCount);
|
||||||
nextInput.setAttribute('name', name);
|
nextInput.setAttribute('name', name);
|
||||||
nextInput.setAttribute('type', 'file');
|
nextInput.setAttribute('type', 'file');
|
||||||
cont.appendChild(nextInput);
|
cont.appendChild(nextInput);
|
||||||
@ -132,6 +148,39 @@
|
|||||||
setup();
|
setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// to remove previously uploaded files
|
||||||
|
window.utils.reactiveFileCheckbox = function(input, label, parent) {
|
||||||
|
// adds eventlistener(s)
|
||||||
|
function addListener(container) {
|
||||||
|
container.addEventListener('click', function() {
|
||||||
|
input.click();
|
||||||
|
});
|
||||||
|
input.addEventListener('change', function(event) {
|
||||||
|
container.classList.toggle('file-checkbox__container--valid', this.checked);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// initial setup
|
||||||
|
function setup() {
|
||||||
|
var cont = input.parentNode;
|
||||||
|
while (cont !== document.body) {
|
||||||
|
if (cont.classList.contains('file-checkbox__container')) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cont = cont.parentNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// take care of properly moving elements
|
||||||
|
if (input.parentNode.classList.contains('checkbox')) {
|
||||||
|
input.parentNode.classList.add('file-checkbox__checkbox');
|
||||||
|
} else {
|
||||||
|
input.classList.add('file-checkbox__checkbox');
|
||||||
|
}
|
||||||
|
addListener(cont);
|
||||||
|
}
|
||||||
|
setup();
|
||||||
|
}
|
||||||
|
|
||||||
window.utils.reactiveFormGroup = function(formGroup, input) {
|
window.utils.reactiveFormGroup = function(formGroup, input) {
|
||||||
// updates to dom
|
// updates to dom
|
||||||
if (input.value.length > 0) {
|
if (input.value.length > 0) {
|
||||||
@ -156,18 +205,28 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
// setup reactive labels
|
// setup reactive labels
|
||||||
Array.from(document.querySelectorAll('.reactive-label')).forEach(function(label) {
|
Array.from(document.querySelectorAll('.reactive-label')).forEach(function(label) {
|
||||||
var input = document.querySelector('#' + label.getAttribute('for'));
|
var input = document.querySelector('#' + label.getAttribute('for'));
|
||||||
|
if (!input) {
|
||||||
|
console.error('No input found for ReactiveLabel! Targeted input: \'#%s\'', label.getAttribute('for'));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
var parent = label.parentElement;
|
var parent = label.parentElement;
|
||||||
var type = input.getAttribute('type');
|
var type = input.getAttribute('type');
|
||||||
var isFileInput = /file/i.test(type);
|
var isFileUpload = /file/i.test(type);
|
||||||
|
var isFileCheckbox = input.classList.contains('file-checkbox');
|
||||||
var isListening = !RegExp(['date', 'checkbox', 'radio', 'hidden', 'file'].join('|')).test(type);
|
var isListening = !RegExp(['date', 'checkbox', 'radio', 'hidden', 'file'].join('|')).test(type);
|
||||||
var isInFormGroup = parent.classList.contains('form-group') && parent.classList.contains('form-group--required');
|
var isInFormGroup = parent.classList.contains('form-group') && parent.classList.contains('form-group--required');
|
||||||
|
|
||||||
|
|
||||||
if (isInFormGroup) {
|
if (isInFormGroup) {
|
||||||
window.utils.reactiveFormGroup(parent, input);
|
window.utils.reactiveFormGroup(parent, input);
|
||||||
}
|
}
|
||||||
if (isFileInput) {
|
if (isFileUpload) {
|
||||||
window.utils.reactiveFileUpload(input, parent);
|
window.utils.reactiveFileUpload(input, parent);
|
||||||
}
|
}
|
||||||
|
if (isFileCheckbox) {
|
||||||
|
window.utils.reactiveFileCheckbox(input, label, parent);
|
||||||
|
}
|
||||||
if (isListening) {
|
if (isListening) {
|
||||||
window.utils.reactiveInputLabel(input, label);
|
window.utils.reactiveInputLabel(input, label);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -3,6 +3,53 @@ form {
|
|||||||
margin: 20px 0;
|
margin: 20px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* FORM GROUPS */
|
||||||
|
.form-group {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 25% max-content;
|
||||||
|
grid-auto-columns: 25%;
|
||||||
|
grid-gap: 5px;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
margin: 10px 0;
|
||||||
|
padding-left: 10px;
|
||||||
|
border-left: 8px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group--required {
|
||||||
|
border-left: 8px solid var(--lighterbase);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group--valid {
|
||||||
|
border-left: 8px solid var(--validbase);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group--has-error {
|
||||||
|
border-left: 8px solid var(--errorbase) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group__label {
|
||||||
|
width: 25%;
|
||||||
|
white-space: nowrap;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 999px) {
|
||||||
|
.form-group {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
grid-template-rows: 30px;
|
||||||
|
align-items: baseline;
|
||||||
|
margin-top: 17px;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
> * {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* TEXT INPUTS */
|
/* TEXT INPUTS */
|
||||||
input[type="text"],
|
input[type="text"],
|
||||||
input[type="password"],
|
input[type="password"],
|
||||||
@ -18,7 +65,7 @@ input[type="email"] {
|
|||||||
color: var(--fontbase);
|
color: var(--fontbase);
|
||||||
transition: all .1s;
|
transition: all .1s;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
min-width: 300px;
|
min-width: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="text"]:focus,
|
input[type="text"]:focus,
|
||||||
@ -37,7 +84,7 @@ textarea {
|
|||||||
outline: 0;
|
outline: 0;
|
||||||
border: 0;
|
border: 0;
|
||||||
padding: 7px 4px;
|
padding: 7px 4px;
|
||||||
min-width: 300px;
|
min-width: 400px;
|
||||||
min-height: 100px;
|
min-height: 100px;
|
||||||
font-family: var(--fontfamilybase);
|
font-family: var(--fontfamilybase);
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
@ -51,30 +98,6 @@ textarea:focus {
|
|||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
border-bottom-color: var(--lightbase);
|
border-bottom-color: var(--lightbase);
|
||||||
}
|
}
|
||||||
/* FORM GROUPS */
|
|
||||||
.form-group {
|
|
||||||
position: relative;
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(2, minmax(150px, max-content));
|
|
||||||
grid-auto-columns: minmax(150px, max-content);
|
|
||||||
grid-gap: 5px;
|
|
||||||
align-items: center;
|
|
||||||
margin: 10px 0;
|
|
||||||
padding-left: 10px;
|
|
||||||
border-left: 8px solid transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group--required {
|
|
||||||
border-left: 8px solid var(--lighterbase);
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group--valid {
|
|
||||||
border-left: 8px solid var(--validbase);
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group--has-error {
|
|
||||||
border-left: 8px solid var(--errorbase) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* CUSTOM LEGACY CHECKBOX AND RADIO BOXES */
|
/* CUSTOM LEGACY CHECKBOX AND RADIO BOXES */
|
||||||
input[type="checkbox"] {
|
input[type="checkbox"] {
|
||||||
@ -115,7 +138,6 @@ input[type="checkbox"]:checked::after {
|
|||||||
.checkbox,
|
.checkbox,
|
||||||
.radio {
|
.radio {
|
||||||
position: relative;
|
position: relative;
|
||||||
margin: 3px;
|
|
||||||
|
|
||||||
> [type="checkbox"],
|
> [type="checkbox"],
|
||||||
> [type="radio"] {
|
> [type="radio"] {
|
||||||
@ -124,8 +146,9 @@ input[type="checkbox"]:checked::after {
|
|||||||
|
|
||||||
> label {
|
> label {
|
||||||
display: block;
|
display: block;
|
||||||
padding: 7px 13px 7px 30px;
|
height: 30px;
|
||||||
background-color: var(--darkbase);
|
width: 30px;
|
||||||
|
background-color: var(--greybase);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
color: white;
|
color: white;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@ -135,8 +158,8 @@ input[type="checkbox"]:checked::after {
|
|||||||
> label::after {
|
> label::after {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 15px;
|
top: 14px;
|
||||||
left: 4px;
|
left: 5px;
|
||||||
display: block;
|
display: block;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
@ -147,13 +170,13 @@ input[type="checkbox"]:checked::after {
|
|||||||
> label::before {
|
> label::before {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 2px;
|
height: 2px;
|
||||||
transform: scale(0.8, 0.1);
|
transform: scale(0.1, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
> label::after {
|
> label::after {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 2px;
|
height: 2px;
|
||||||
transform: scale(0.8, 0.1);
|
transform: scale(0.1, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
> :checked + label {
|
> :checked + label {
|
||||||
@ -161,12 +184,27 @@ input[type="checkbox"]:checked::after {
|
|||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover > label::before,
|
&:hover > label {
|
||||||
|
background-color: var(--lighterbase);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover > label::before {
|
||||||
|
transform: scale(0.8, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
> :checked + label::before {
|
> :checked + label::before {
|
||||||
transform: scale(1, 1) rotate(45deg);
|
transform: scale(1, 1) rotate(45deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover > label::after,
|
> :checked + label:hover::after,
|
||||||
|
> :checked + label:hover::before {
|
||||||
|
transform: scale(1, 1) rotate(0deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover > label::after {
|
||||||
|
transform: scale(0.8, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
> :checked + label::after {
|
> :checked + label::after {
|
||||||
transform: scale(1, 1) rotate(-45deg);
|
transform: scale(1, 1) rotate(-45deg);
|
||||||
}
|
}
|
||||||
@ -202,12 +240,6 @@ input[type="checkbox"]:checked::after {
|
|||||||
color: var(--fontbase);
|
color: var(--fontbase);
|
||||||
}
|
}
|
||||||
@media (max-width: 999px) {
|
@media (max-width: 999px) {
|
||||||
.form-group {
|
|
||||||
grid-template-rows: 30px;
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
align-items: baseline;
|
|
||||||
margin-top: 17px;
|
|
||||||
}
|
|
||||||
.reactive-label {
|
.reactive-label {
|
||||||
position: relative;
|
position: relative;
|
||||||
transform: translate(2px, 30px);
|
transform: translate(2px, 30px);
|
||||||
@ -220,7 +252,7 @@ input[type="checkbox"]:checked::after {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* CUSTOM FILE INPUT */
|
/* CUSTOM FILE INPUT */
|
||||||
input[type="file"] {
|
input[type="file"].js-file-input {
|
||||||
color: white;
|
color: white;
|
||||||
width: 0.1px;
|
width: 0.1px;
|
||||||
height: 0.1px;
|
height: 0.1px;
|
||||||
@ -231,25 +263,34 @@ input[type="file"] {
|
|||||||
outline: 0;
|
outline: 0;
|
||||||
border: 0;
|
border: 0;
|
||||||
}
|
}
|
||||||
.file-input__container {
|
.file-input__container,
|
||||||
|
.file-checkbox__container {
|
||||||
grid-column-start: 2;
|
grid-column-start: 2;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
.file-input__label,
|
.file-input__label,
|
||||||
.file-input__remover {
|
.file-input__remover,
|
||||||
|
.file-checkbox__label,
|
||||||
|
.file-checkbox__remover {
|
||||||
display: block;
|
display: block;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
padding: 5px 13px;
|
padding: 5px 13px;
|
||||||
color: var(--whitebase);
|
color: var(--whitebase);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.file-input__label {
|
.file-input__label,
|
||||||
|
.file-checkbox__label {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
}
|
}
|
||||||
.file-input__label.btn {
|
.file-checkbox__label {
|
||||||
|
background-color: var(--greybase);
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
.file-input__label.btn,
|
||||||
|
.file-checkbox__label.btn {
|
||||||
padding: 5px 13px;
|
padding: 5px 13px;
|
||||||
}
|
}
|
||||||
.file-input__label::after,
|
.file-input__label::after,
|
||||||
@ -271,6 +312,9 @@ input[type="file"] {
|
|||||||
.file-input__label::before {
|
.file-input__label::before {
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
}
|
}
|
||||||
|
.file-checkbox__checkbox {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
.file-input__remover {
|
.file-input__remover {
|
||||||
display: none;
|
display: none;
|
||||||
width: 40px;
|
width: 40px;
|
||||||
@ -292,6 +336,15 @@ input[type="file"] {
|
|||||||
.file-input__container--valid > .file-input__label {
|
.file-input__container--valid > .file-input__label {
|
||||||
background-color: var(--lightbase);
|
background-color: var(--lightbase);
|
||||||
}
|
}
|
||||||
|
.file-checkbox__container--valid > .file-checkbox__label {
|
||||||
|
text-decoration: none;
|
||||||
|
background-color: var(--lighterbase);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--greybase);
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
}
|
||||||
.file-input__container--valid > .file-input__label::before,
|
.file-input__container--valid > .file-input__label::before,
|
||||||
.file-input__container--valid > .file-input__label::after {
|
.file-input__container--valid > .file-input__label::after {
|
||||||
content: none;
|
content: none;
|
||||||
@ -300,7 +353,8 @@ input[type="file"] {
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
@media (max-width: 999px) {
|
@media (max-width: 999px) {
|
||||||
.file-input__container {
|
.file-input__container,
|
||||||
|
.file-checkbox__container {
|
||||||
grid-column-start: 1;
|
grid-column-start: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1
templates/standalone/modal.hamlet
Normal file
1
templates/standalone/modal.hamlet
Normal file
@ -0,0 +1 @@
|
|||||||
|
<!-- only here to be able to include modal using `toWidget` -->
|
||||||
100
templates/standalone/modal.julius
Normal file
100
templates/standalone/modal.julius
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
(function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
window.utils = window.utils || {};
|
||||||
|
|
||||||
|
window.utils.modal = function(modal) {
|
||||||
|
var overlay = document.createElement('div');
|
||||||
|
var closer = document.createElement('div');
|
||||||
|
var trigger = document.querySelector(modal.dataset.trigger);
|
||||||
|
var origParent = modal.parentNode;
|
||||||
|
|
||||||
|
function open(event) {
|
||||||
|
if (event) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
modal.classList.add('modal--open');
|
||||||
|
overlay.classList.add('modal__overlay');
|
||||||
|
document.body.insertBefore(modal, null);
|
||||||
|
document.body.insertBefore(overlay, modal);
|
||||||
|
overlay.classList.add('modal__overlay--open');
|
||||||
|
toggleScroll(false);
|
||||||
|
|
||||||
|
if (modal.dataset.closeable === 'true') {
|
||||||
|
closer.classList.add('modal__closer');
|
||||||
|
modal.insertBefore(closer, null);
|
||||||
|
closer.addEventListener('click', close, false);
|
||||||
|
overlay.addEventListener('click', close, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// open this modal with an event:
|
||||||
|
// document.dispatchEvent(new CustomEvent('modal-open', { dateils: {for: 'modal-13'}}))
|
||||||
|
function openOnEvent(event) {
|
||||||
|
if (event.detail.for === modal.getAttribute('id')) {
|
||||||
|
open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function close(event) {
|
||||||
|
if (typeof event === 'undefined' || event.target === closer || event.target === overlay) {
|
||||||
|
overlay.remove();
|
||||||
|
origParent.insertBefore(modal, null);
|
||||||
|
modal.classList.remove('modal--open');
|
||||||
|
toggleScroll(true);
|
||||||
|
closer.removeEventListener('click', close, false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function setup() {
|
||||||
|
// every modal can be openend via document-wide event, see openOnEvent
|
||||||
|
document.addEventListener('modal-open', openOnEvent, false);
|
||||||
|
// if modal has trigger assigned to it open modal on click
|
||||||
|
if (trigger) {
|
||||||
|
trigger.classList.add('modal__trigger');
|
||||||
|
trigger.addEventListener('click', open, false);
|
||||||
|
}
|
||||||
|
// if there is no content specified for the modal we assume that
|
||||||
|
// the content is supposed to be the page the trigger links to.
|
||||||
|
// so we check if the trigger has a href-attribute, fetch that page
|
||||||
|
// and replace the modal content with the response
|
||||||
|
var replaceMe = modal.querySelector('.replace-me');
|
||||||
|
var replaceWith = trigger ? trigger.getAttribute('href') : '';
|
||||||
|
if (replaceMe) {
|
||||||
|
replaceMe.classList.remove('replace-me');
|
||||||
|
replaceMe.innerText = '...loading';
|
||||||
|
if (replaceWith.length > 0) {
|
||||||
|
fetch(replaceWith).then(function(response) {
|
||||||
|
return response.text();
|
||||||
|
}).then(function(body) {
|
||||||
|
var modalContent = document.createElement('div');
|
||||||
|
modalContent.innerHTML = body;
|
||||||
|
var main = modalContent.querySelector('.main__content');
|
||||||
|
if (main) {
|
||||||
|
replaceMe.innerText = '';
|
||||||
|
replaceMe.insertBefore(main, null);
|
||||||
|
} else {
|
||||||
|
replaceMe.innerHTML = body;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// tell further modals, that this one already got initialized
|
||||||
|
modal.classList.add('js-modal-initialized');
|
||||||
|
}
|
||||||
|
setup();
|
||||||
|
};
|
||||||
|
|
||||||
|
// make sure document doesn't scroll when modal is active
|
||||||
|
function toggleScroll(scrollable) {
|
||||||
|
document.body.classList.toggle('no-scroll', !scrollable);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
|
||||||
|
Array.from(document.querySelectorAll('.js-modal:not(.js-modal-initialized)')).map(function(modal) {
|
||||||
|
new utils.modal(modal);
|
||||||
|
});
|
||||||
|
|
||||||
|
}, false);
|
||||||
87
templates/standalone/modal.lucius
Normal file
87
templates/standalone/modal.lucius
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
.modal {
|
||||||
|
position: fixed;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
transform: translate(-50%, -50%) scale(0.8, 0.8);
|
||||||
|
display: block;
|
||||||
|
background-color: rgba(255, 255, 255, 0.9);
|
||||||
|
min-width: 60vw;
|
||||||
|
min-height: 100px;
|
||||||
|
max-height: calc(100vh - 30px);
|
||||||
|
border-radius: 7px;
|
||||||
|
z-index: -1;
|
||||||
|
color: var(--fontbase);
|
||||||
|
padding: 20px;
|
||||||
|
overflow: auto;
|
||||||
|
opacity: 0;
|
||||||
|
transition: all .15s ease;
|
||||||
|
|
||||||
|
&.modal--open {
|
||||||
|
opacity: 1;
|
||||||
|
z-index: 200;
|
||||||
|
transform: translate(-50%, -50%) scale(1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 999px) {
|
||||||
|
.modal {
|
||||||
|
min-width: 80vw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (max-width: 666px) {
|
||||||
|
.modal {
|
||||||
|
min-width: 90vw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (max-width: 444px) {
|
||||||
|
.modal {
|
||||||
|
min-width: calc(100vw - 20px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal__overlay {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
background-color: transparent;
|
||||||
|
z-index: -1;
|
||||||
|
opacity: 0;
|
||||||
|
transition: all .2s ease;
|
||||||
|
|
||||||
|
&.modal__overlay--open {
|
||||||
|
z-index: 199;
|
||||||
|
opacity: 1;
|
||||||
|
background-color: rgba(0, 0, 0, 0.4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal__trigger {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal__closer {
|
||||||
|
position: absolute;
|
||||||
|
top: 20px;
|
||||||
|
right: 20px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
background-color: var(--darkbase);
|
||||||
|
border-radius: 2px;
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 20;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '\e014';
|
||||||
|
font-family: 'Glyphicons Halflings';
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-scroll {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
@ -1,88 +1,107 @@
|
|||||||
/**
|
/**
|
||||||
* delcare a table as sortable by adding class 'js-sortable'
|
* delcare a table as sortable by adding class 'js-sortable'
|
||||||
*/
|
*/
|
||||||
|
(function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
window.utils = window.utils || {};
|
||||||
|
|
||||||
|
window.utils.sortable = function(table) {
|
||||||
|
var ASC = 1;
|
||||||
|
var DESC = -1;
|
||||||
|
|
||||||
|
var trs, ths, sortBy, sortDir, trContents;
|
||||||
|
|
||||||
|
function setup() {
|
||||||
|
trs = table.querySelectorAll('tr');
|
||||||
|
ths = table.querySelectorAll('th');
|
||||||
|
sortBy = 0;
|
||||||
|
sortDir = ASC;
|
||||||
|
trContents = [];
|
||||||
|
|
||||||
|
Array.from(trs).forEach(function(tr, rowIndex) {
|
||||||
|
if (rowIndex === 0) {
|
||||||
|
// register table headers as sort-listener
|
||||||
|
Array.from(tr.querySelectorAll('th')).forEach(function(th, thIndex) {
|
||||||
|
th.addEventListener('click', function(el) {
|
||||||
|
sortTableBy(thIndex);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// register table rows
|
||||||
|
trContents.push(Array.from(tr.querySelectorAll('td')).map(function(td) {
|
||||||
|
return td.innerHTML;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setup();
|
||||||
|
|
||||||
|
function updateThs(thIndex, sortOrder) {
|
||||||
|
Array.from(ths).forEach(function (th) {
|
||||||
|
th.classList.remove('sorted-asc', 'sorted-desc');
|
||||||
|
});
|
||||||
|
var suffix = sortOrder > 0 ? 'asc' : 'desc';
|
||||||
|
ths[thIndex].classList.add('sorted-' + suffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortTableBy(thIndex) {
|
||||||
|
var sortKey = thIndex;
|
||||||
|
var sortOrder = ASC;
|
||||||
|
if (sortBy === sortKey) {
|
||||||
|
sortOrder = sortDir === ASC ? DESC : ASC;
|
||||||
|
}
|
||||||
|
|
||||||
|
trContents.sort(dynamicSortByType(sortKey, sortOrder));
|
||||||
|
trContents.sort(dynamicSortByKey(sortKey, sortOrder));
|
||||||
|
sortBy = thIndex;
|
||||||
|
sortDir = sortOrder;
|
||||||
|
updateThs(thIndex, sortOrder);
|
||||||
|
|
||||||
|
Array.from(trs).forEach(function(tr, trIndex) {
|
||||||
|
if (trIndex > 0) {
|
||||||
|
Array.from(tr.querySelectorAll('td')).forEach(function (td, tdIndex) {
|
||||||
|
td.innerHTML = trContents[trIndex - 1][tdIndex];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function dynamicSortByKey(key, order) {
|
||||||
|
return function (a,b) {
|
||||||
|
var aVal = parseInt(a[key]);
|
||||||
|
var bVal = parseInt(b[key]);
|
||||||
|
if ((isNaN(aVal) && !isNaN(bVal)) || (!isNaN(aVal) && isNaN(bVal))) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
aVal = isNaN(aVal) ? a[key] : aVal;
|
||||||
|
bVal = isNaN(bVal) ? b[key] : bVal;
|
||||||
|
var result = (aVal < bVal) ? -1 : (aVal > bVal) ? 1 : 0;
|
||||||
|
return result * order;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function dynamicSortByType(key, order) {
|
||||||
|
return function (a,b) {
|
||||||
|
var aVal = parseInt(a[key]);
|
||||||
|
var bVal = parseInt(b[key]);
|
||||||
|
aVal = isNaN(aVal) ? a[key] : aVal;
|
||||||
|
bVal = isNaN(bVal) ? b[key] : bVal;
|
||||||
|
var res = (aVal < bVal ? -1 : aVal > bVal ? 1 : 0);
|
||||||
|
if (isNaN(aVal) && !isNaN(bVal)) {
|
||||||
|
res = -1;
|
||||||
|
}
|
||||||
|
if (!isNaN(aVal) && isNaN(bVal)) {
|
||||||
|
res = 1;
|
||||||
|
}
|
||||||
|
return res * order;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
Array.from(document.querySelectorAll('.js-sortable')).forEach(function(table) {
|
||||||
var tables = [];
|
utils.sortable(table);
|
||||||
var ASC = 1;
|
|
||||||
var DESC = -1;
|
|
||||||
|
|
||||||
function initTable(table, tableIndex) {
|
|
||||||
var trs = table.querySelectorAll('tr');
|
|
||||||
var ths = table.querySelectorAll('th');
|
|
||||||
var trContents = [];
|
|
||||||
|
|
||||||
Array.from(trs).forEach(function(tr, rowIndex) {
|
|
||||||
if (rowIndex === 0) {
|
|
||||||
// register table headers as sort-listener
|
|
||||||
Array.from(tr.querySelectorAll('th')).forEach(function(th, thIndex) {
|
|
||||||
th.addEventListener('click', function(el) {
|
|
||||||
sortTableBy(tableIndex, thIndex);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// register table rows
|
|
||||||
trContents.push(Array.from(tr.querySelectorAll('td')).map(function(td) {
|
|
||||||
return td.innerHTML;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
tables.push({
|
|
||||||
el: table,
|
|
||||||
ths: ths,
|
|
||||||
sortBy: 0,
|
|
||||||
sortDir: ASC,
|
|
||||||
trContents,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateThs(tableIndex, thIndex, sortOrder) {
|
|
||||||
Array.from(tables[tableIndex].ths).forEach(function (th) {
|
|
||||||
th.classList.remove('sorted-asc', 'sorted-desc');
|
|
||||||
});
|
|
||||||
var suffix = sortOrder > 0 ? 'asc' : 'desc';
|
|
||||||
tables[tableIndex].ths[thIndex].classList.add('sorted-' + suffix);
|
|
||||||
}
|
|
||||||
|
|
||||||
function sortTableBy(tableIndex, thIndex) {
|
|
||||||
var table = tables[tableIndex];
|
|
||||||
var sortKey = thIndex;
|
|
||||||
var sortOrder = ASC;
|
|
||||||
if (table.sortBy === sortKey) {
|
|
||||||
sortOrder = table.sortDir === ASC ? DESC : ASC;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.trContents.sort(dynamicSort(sortKey, sortOrder));
|
|
||||||
tables[tableIndex].sortBy = thIndex;
|
|
||||||
tables[tableIndex].sortDir = sortOrder;
|
|
||||||
updateThs(tableIndex, thIndex, sortOrder);
|
|
||||||
|
|
||||||
Array.from(table.el.querySelectorAll('tr')).forEach(function(tr, trIndex) {
|
|
||||||
if (trIndex > 0) {
|
|
||||||
Array.from(tr.querySelectorAll('td')).forEach(function (td, tdIndex) {
|
|
||||||
td.innerHTML = table.trContents[trIndex - 1][tdIndex];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function dynamicSort(key, order) {
|
|
||||||
return function (a,b) {
|
|
||||||
var aVal = parseInt(a[key]);
|
|
||||||
var bVal = parseInt(b[key]);
|
|
||||||
if ((isNaN(aVal) && !isNaN(bVal)) || (!isNaN(aVal) && isNaN(bVal))) {
|
|
||||||
console.error('trying to sort table by row with mixed content: "%s", "%s"', a[key], b[key]);
|
|
||||||
}
|
|
||||||
aVal = isNaN(aVal) ? a[key] : aVal;
|
|
||||||
bVal = isNaN(bVal) ? b[key] : bVal;
|
|
||||||
var result = (aVal < bVal) ? -1 : (aVal > bVal) ? 1 : 0;
|
|
||||||
return result * order;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var rawTables = document.querySelectorAll('.js-sortable');
|
|
||||||
Array.from(rawTables).forEach(function(table, i) {
|
|
||||||
initTable(table, i);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
$newline never
|
||||||
<aside .main__aside>
|
<aside .main__aside>
|
||||||
<div .asidenav>
|
<div .asidenav>
|
||||||
<div .asidenav__box--dont-hide>
|
<div .asidenav__box--dont-hide>
|
||||||
@ -20,13 +21,28 @@
|
|||||||
<a .asidenav__link-wrapper href="/course/S2018/ixd/show">
|
<a .asidenav__link-wrapper href="/course/S2018/ixd/show">
|
||||||
<div .asidenav__link-triple>IXD
|
<div .asidenav__link-triple>IXD
|
||||||
<div .asidenav__link-label>Interaction Design
|
<div .asidenav__link-label>Interaction Design
|
||||||
|
<ul .asidenav__nested-list>
|
||||||
|
<li .asidenav__list-item>
|
||||||
|
<a .asidenav__link-wrapper href="/course/S2018/ixd/ex">Übungsblätter
|
||||||
|
<li .asidenav__list-item>
|
||||||
|
<a .asidenav__link-wrapper href="/course/S2018/ixd/show">Klausuren
|
||||||
|
<li .asidenav__list-item>
|
||||||
|
<a .asidenav__link-wrapper href="/course/S2018/ixd/show">Übungsgruppen
|
||||||
<li .asidenav__list-item>
|
<li .asidenav__list-item>
|
||||||
<a .asidenav__link-wrapper href="/course/S2018/ffp/show">
|
<a .asidenav__link-wrapper href="/course/S2018/ffp/show">
|
||||||
<div .asidenav__link-triple>FFP
|
<div .asidenav__link-triple>FFP
|
||||||
<div .asidenav__link-label>Fortgeschrittene Funktionale Programmierung
|
<div .asidenav__link-label>Fortgeschrittene Funktionale Programmierung
|
||||||
|
<ul .asidenav__nested-list>
|
||||||
|
<li .asidenav__list-item>
|
||||||
|
<a .asidenav__link-wrapper href="/course/S2018/ffp/ex">Abgaben
|
||||||
|
<li .asidenav__list-item>
|
||||||
|
<a .asidenav__link-wrapper href="/course/S2018/ffp/show">Klausuren
|
||||||
<li .asidenav__list-item>
|
<li .asidenav__list-item>
|
||||||
<a .asidenav__link-wrapper href="/course/S2018/dbs/show">
|
<a .asidenav__link-wrapper href="/course/S2018/dbs/show">
|
||||||
<div .asidenav__link-triple>DBS
|
<div .asidenav__link-triple>DBS
|
||||||
<div .asidenav__link-label>Datenbanksysteme
|
<div .asidenav__link-label>Datenbanksysteme
|
||||||
|
<ul .asidenav__nested-list>
|
||||||
|
<li .asidenav__list-item>
|
||||||
|
<a .asidenav__link-wrapper href="/course/S2018/dbs/ex">Übungsgruppen
|
||||||
|
|
||||||
<div .asidenav__toggler>
|
<div .asidenav__toggler>
|
||||||
|
|||||||
@ -3,9 +3,18 @@
|
|||||||
|
|
||||||
window.utils = window.utils || {};
|
window.utils = window.utils || {};
|
||||||
|
|
||||||
window.utils.aside = function(asideEl, topNav) {
|
// Defines a function to turn an element into an interactive aside-navigation.
|
||||||
|
// If the small is smaller than 999px the navigation is automatically
|
||||||
|
// collapsed - even when dynamically resized (e.g. switching from portatit
|
||||||
|
// to landscape).
|
||||||
|
// The can user may also manually collapse and expand the navigation by
|
||||||
|
// using the little arrow at the bottom.
|
||||||
|
window.utils.aside = function(asideEl) {
|
||||||
var collapsed = false;
|
var collapsed = false;
|
||||||
var collClass = 'main__aside--collapsed';
|
var collClass = 'main__aside--collapsed';
|
||||||
|
// animClass used to enable transitions only when needed so that
|
||||||
|
// (potentially happening) initial collapse of the asidenav
|
||||||
|
// goes unnoticed by the user.
|
||||||
var animClass = 'main__aside--transitioning';
|
var animClass = 'main__aside--transitioning';
|
||||||
var aboveCollapsedNav = false;
|
var aboveCollapsedNav = false;
|
||||||
|
|
||||||
@ -15,24 +24,14 @@
|
|||||||
if (document.body.getBoundingClientRect().width < 999 || collLS) {
|
if (document.body.getBoundingClientRect().width < 999 || collLS) {
|
||||||
asideEl.classList.add(collClass);
|
asideEl.classList.add(collClass);
|
||||||
collapsed = true;
|
collapsed = true;
|
||||||
if (topNav) {
|
|
||||||
topNav.style.paddingLeft = '90px';
|
|
||||||
window.setTimeout(function() {
|
|
||||||
topNav.classList.add('navbar--animated');
|
|
||||||
}, 200);
|
|
||||||
}
|
|
||||||
} else if (topNav) {
|
|
||||||
topNav.classList.add('navbar--animated');
|
|
||||||
}
|
}
|
||||||
|
addListener();
|
||||||
}
|
}
|
||||||
|
|
||||||
function check() {
|
function check() {
|
||||||
if (collapsed && !hasClass() || !collapsed && hasClass()) {
|
if (collapsed && !hasClass() || !collapsed && hasClass()) {
|
||||||
asideEl.classList.add(animClass);
|
asideEl.classList.add(animClass);
|
||||||
asideEl.classList.toggle(collClass, collapsed);
|
asideEl.classList.toggle(collClass, collapsed);
|
||||||
if (topNav) {
|
|
||||||
topNav.style.paddingLeft = collapsed ? '90px' : '';
|
|
||||||
}
|
|
||||||
window.localStorage.setItem('asidenavCollapsed', collapsed);
|
window.localStorage.setItem('asidenavCollapsed', collapsed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -41,41 +40,46 @@
|
|||||||
return asideEl.classList.contains(collClass);
|
return asideEl.classList.contains(collClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
asideEl.querySelector('.asidenav__toggler').addEventListener('click', function(event) {
|
function addListener() {
|
||||||
collapsed = !collapsed;
|
|
||||||
check();
|
|
||||||
}, false);
|
|
||||||
asideEl.addEventListener('transitionend', function(event) {
|
|
||||||
if (event.propertyName === 'opacity') {
|
|
||||||
asideEl.classList.remove(animClass);
|
|
||||||
}
|
|
||||||
}, false);
|
|
||||||
window.addEventListener('resize', function() {
|
|
||||||
collapsed = document.body.getBoundingClientRect().width < 999;
|
|
||||||
check();
|
|
||||||
}, false);
|
|
||||||
|
|
||||||
asideEl.addEventListener('mouseover', function(event) {
|
asideEl.querySelector('.asidenav__toggler').addEventListener('click', function(event) {
|
||||||
if (!collapsed) {
|
collapsed = !collapsed;
|
||||||
return false;
|
check();
|
||||||
}
|
}, false);
|
||||||
aboveCollapsedNav = true;
|
|
||||||
console.log(event);
|
asideEl.addEventListener('transitionend', function(event) {
|
||||||
window.setTimeout(function() {
|
if (event.propertyName === 'opacity') {
|
||||||
if (aboveCollapsedNav && !document.body.classList.contains('touch-supported')) {
|
asideEl.classList.remove(animClass);
|
||||||
asideEl.classList.add('pseudo-hover');
|
|
||||||
}
|
}
|
||||||
}, 430);
|
}, false);
|
||||||
}, false);
|
|
||||||
asideEl.addEventListener('mouseleave', function(event) {
|
window.addEventListener('resize', function() {
|
||||||
aboveCollapsedNav = false;
|
collapsed = document.body.getBoundingClientRect().width < 999;
|
||||||
asideEl.classList.remove('pseudo-hover');
|
check();
|
||||||
}, false);
|
}, false);
|
||||||
|
|
||||||
|
asideEl.addEventListener('mouseover', function(event) {
|
||||||
|
if (!collapsed) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
aboveCollapsedNav = true;
|
||||||
|
window.setTimeout(function() {
|
||||||
|
if (aboveCollapsedNav && !document.body.classList.contains('touch-supported')) {
|
||||||
|
asideEl.classList.add('pseudo-hover');
|
||||||
|
}
|
||||||
|
}, 800);
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
asideEl.addEventListener('mouseleave', function(event) {
|
||||||
|
aboveCollapsedNav = false;
|
||||||
|
asideEl.classList.remove('pseudo-hover');
|
||||||
|
}, false);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
|
||||||
utils.aside(document.querySelector('.main__aside'), document.querySelector('.navbar'));
|
utils.aside(document.querySelector('.main__aside'));
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@ -4,7 +4,6 @@
|
|||||||
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
flex: 0 0 300px;
|
flex: 0 0 300px;
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
.main__aside--transitioning {
|
.main__aside--transitioning {
|
||||||
transition: flex-basis .2s ease;
|
transition: flex-basis .2s ease;
|
||||||
@ -19,6 +18,7 @@
|
|||||||
.main__aside--collapsed {
|
.main__aside--collapsed {
|
||||||
width: 50px;
|
width: 50px;
|
||||||
flex-basis: 50px;
|
flex-basis: 50px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
.asidenav__box-title {
|
.asidenav__box-title {
|
||||||
width: 50px;
|
width: 50px;
|
||||||
@ -44,6 +44,9 @@
|
|||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
color: white;
|
color: white;
|
||||||
|
|
||||||
|
.js-show-hide__target {
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
.js-show-hide__toggle::before {
|
.js-show-hide__toggle::before {
|
||||||
top: 14px;
|
top: 14px;
|
||||||
right: 12px;
|
right: 12px;
|
||||||
@ -76,6 +79,39 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.asidenav__nested-list {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
color: var(--fontbase);
|
||||||
|
transform: translateX(0);
|
||||||
|
opacity: 0;
|
||||||
|
transition: all .2s ease;
|
||||||
|
z-index: -1;
|
||||||
|
|
||||||
|
.asidenav__list-item {
|
||||||
|
background-color: var(--darkbase);
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.asidenav__link-wrapper {
|
||||||
|
padding-left: 13px;
|
||||||
|
padding-right: 13px;
|
||||||
|
border-left: 20px solid white;
|
||||||
|
transition: all .2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: white;
|
||||||
|
color: var(--darkbase) !important;
|
||||||
|
border-left: 20px solid var(--darkbase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.asidenav__list-item {
|
.asidenav__list-item {
|
||||||
position: relative;
|
position: relative;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
@ -86,6 +122,11 @@
|
|||||||
color: white;
|
color: white;
|
||||||
background-color: var(--darkbase);
|
background-color: var(--darkbase);
|
||||||
|
|
||||||
|
.asidenav__nested-list {
|
||||||
|
transform: translateX(100%);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.asidenav__link-wrapper,
|
.asidenav__link-wrapper,
|
||||||
.asidenav__link-label {
|
.asidenav__link-label {
|
||||||
color: white;
|
color: white;
|
||||||
|
|||||||
18
templates/widgets/modal.hamlet
Normal file
18
templates/widgets/modal.hamlet
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<div .modal.js-modal #modal-#{modalId} data-trigger=#{modalTrigger} data-closeable=true>
|
||||||
|
$if 11 == length modalContent
|
||||||
|
<div .replace-me>
|
||||||
|
$else
|
||||||
|
<h2>Neue Veranstaltung
|
||||||
|
#{modalContent}
|
||||||
|
<form>
|
||||||
|
<div .form-group>
|
||||||
|
<label .reactive-label for="inp1">Name
|
||||||
|
<input type="text" id="inp1">
|
||||||
|
<div .form-group>
|
||||||
|
<label .reactive-label for="inp2">Kürzel
|
||||||
|
<input type="text" id="inp2">
|
||||||
|
<div .form-group>
|
||||||
|
<label .reactive-label for="inp3">Semester
|
||||||
|
<input type="text" id="inp3">
|
||||||
|
<div .form-group>
|
||||||
|
<input type="submit" value="Submit">
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
$newline never
|
||||||
<div .navbar-container>
|
<div .navbar-container>
|
||||||
<nav .navbar.js-sticky-navbar>
|
<nav .navbar.js-sticky-navbar>
|
||||||
|
|
||||||
|
|||||||
@ -1,28 +1,48 @@
|
|||||||
/**
|
(function() {
|
||||||
* .js-sticky-navbar
|
'use strict';
|
||||||
* ul
|
|
||||||
* li Item 1
|
window.utils = window.utils || {};
|
||||||
* li Item 2
|
|
||||||
*/
|
window.utils.stickynav = function(nav) {
|
||||||
|
var ticking = false;
|
||||||
|
|
||||||
|
init();
|
||||||
|
function init() {
|
||||||
|
nav.style.paddingLeft = document.body.getBoundingClientRect().width < 999 ? '90px' : '';
|
||||||
|
window.setTimeout(function() {
|
||||||
|
nav.classList.add('navbar--animated');
|
||||||
|
}, 200);
|
||||||
|
checkScroll();
|
||||||
|
addListener();
|
||||||
|
}
|
||||||
|
|
||||||
|
// checks scroll direction and shows/hides navbar accordingly
|
||||||
|
function checkScroll() {
|
||||||
|
var sticky = window.scrollY > 0;
|
||||||
|
nav.classList.toggle('navbar--sticky', sticky);
|
||||||
|
ticking = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addListener() {
|
||||||
|
window.addEventListener('scroll', function(e) {
|
||||||
|
if (!ticking) {
|
||||||
|
window.requestAnimationFrame(checkScroll);
|
||||||
|
ticking = true;
|
||||||
|
}
|
||||||
|
}, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('resize', function() {
|
||||||
|
nav.style.paddingLeft = document.body.getBoundingClientRect().width < 999 ? '90px' : '';
|
||||||
|
}, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
|
||||||
var ticking = false;
|
utils.stickynav(document.querySelector('.js-sticky-navbar'));
|
||||||
var nav = document.querySelector('.js-sticky-navbar');
|
|
||||||
|
|
||||||
window.addEventListener('scroll', function(e) {
|
|
||||||
if (!ticking) {
|
|
||||||
window.requestAnimationFrame(checkScroll);
|
|
||||||
ticking = true;
|
|
||||||
}
|
|
||||||
}, false);
|
|
||||||
|
|
||||||
// checks scroll direction and shows/hides navbar accordingly
|
|
||||||
function checkScroll() {
|
|
||||||
var sticky = window.scrollY > 0;
|
|
||||||
nav.classList.toggle('navbar--sticky', sticky);
|
|
||||||
ticking = false;
|
|
||||||
}
|
|
||||||
checkScroll();
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
11
templates/widgets/pageactionprime.hamlet
Normal file
11
templates/widgets/pageactionprime.hamlet
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
$newline never
|
||||||
|
$if hasPageActions
|
||||||
|
<div .page-nav-prime>
|
||||||
|
<h3>Aktionen:
|
||||||
|
<ul .pagenav__list>
|
||||||
|
$forall menuType <- menuTypes
|
||||||
|
$case menuType
|
||||||
|
$of PageActionPrime (MenuItem label mIcon route _)
|
||||||
|
<li .pagenav__list-item>
|
||||||
|
<a .pagenav__link-wrapper href=@{route}>#{label}
|
||||||
|
$of _
|
||||||
21
templates/widgets/pageactionprime.lucius
Normal file
21
templates/widgets/pageactionprime.lucius
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
.page-nav-prime {
|
||||||
|
background-color: var(--lightgreybase);
|
||||||
|
box-shadow: -20px -20px 0 20px var(--lightgreybase),
|
||||||
|
20px -20px 0 20px var(--lightgreybase);
|
||||||
|
padding: 13px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-nav-prime .pagenav__list {
|
||||||
|
margin: 7px 0 0;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.page-nav-prime .pagenav__list-item {
|
||||||
|
display: inline-block;
|
||||||
|
border-bottom: 2px solid var(--lightbase);
|
||||||
|
margin-right: 7px;
|
||||||
|
transition: border-bottom-color .2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-bottom-color: var(--lighterbase);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user