Course user notes working, but needs model refactor
This commit is contained in:
parent
21ba1f2afa
commit
c9ab64e518
@ -95,6 +95,10 @@ CourseFilterNone: Egal
|
|||||||
CourseDeleteQuestion: Wollen Sie den unten aufgeführten Kurs wirklich löschen?
|
CourseDeleteQuestion: Wollen Sie den unten aufgeführten Kurs wirklich löschen?
|
||||||
CourseDeleted: Kurs gelöscht
|
CourseDeleted: Kurs gelöscht
|
||||||
CourseUserNote: Notiz
|
CourseUserNote: Notiz
|
||||||
|
CourseUserNoteTooltip: Nur für Dozenten dieses Kurses einsehbar
|
||||||
|
CourseUserNoteSaved: Notizänderungen gespeichert
|
||||||
|
CourseUserNoteDeleted: Teilnehmernotiz gelöscht
|
||||||
|
|
||||||
|
|
||||||
NoSuchTerm tid@TermId: Semester #{display tid} gibt es nicht.
|
NoSuchTerm tid@TermId: Semester #{display tid} gibt es nicht.
|
||||||
NoSuchSchool ssh@SchoolId: Institut #{display ssh} gibt es nicht.
|
NoSuchSchool ssh@SchoolId: Institut #{display ssh} gibt es nicht.
|
||||||
|
|||||||
@ -40,12 +40,20 @@ CourseParticipant -- course enrolement
|
|||||||
registration UTCTime -- time of last enrolement for this course
|
registration UTCTime -- time of last enrolement for this course
|
||||||
field StudyFeaturesId Maybe -- associated degree course, user-defined; required for communicating grades
|
field StudyFeaturesId Maybe -- associated degree course, user-defined; required for communicating grades
|
||||||
UniqueParticipant user course
|
UniqueParticipant user course
|
||||||
|
-- Replace the last two by the following, once an audit log is available
|
||||||
|
-- CourseUserNote -- lecturers of a specific course may share a text note on each enrolled student
|
||||||
|
-- course CourseId
|
||||||
|
-- user UserId
|
||||||
|
-- note Html -- arbitrary user-defined text; visible only to lecturer of this course
|
||||||
|
-- time UTCTime -- PROBLEM: deleted note has no modification date
|
||||||
|
-- editor UserId -- who edited this note last
|
||||||
|
-- UniqueCourseUserNote user course
|
||||||
CourseUserNote -- lecturers of a specific course may share a text note on each enrolled student
|
CourseUserNote -- lecturers of a specific course may share a text note on each enrolled student
|
||||||
course CourseId
|
course CourseId
|
||||||
user UserId
|
user UserId
|
||||||
note Text -- arbitrary user-defined text; visible only to lecturer of this course
|
note Text -- arbitrary user-defined text; visible only to lecturer of this course
|
||||||
UniqueCourseUserNotes user course
|
UniqueCourseUserNote user course
|
||||||
CourseUserNoteEdit -- who edited a participants course note whenl
|
CourseUserNoteEdit -- who edited a participants course note when
|
||||||
user UserId
|
user UserId
|
||||||
time UTCTime
|
time UTCTime
|
||||||
note CourseUserNoteId
|
note CourseUserNoteId -- PROBLEM: deleted notes have no modification date any more
|
||||||
|
|||||||
2
routes
2
routes
@ -76,7 +76,7 @@
|
|||||||
/edit CEditR GET POST
|
/edit CEditR GET POST
|
||||||
/delete CDeleteR GET POST !lecturerANDempty
|
/delete CDeleteR GET POST !lecturerANDempty
|
||||||
/users CUsersR GET
|
/users CUsersR GET
|
||||||
/users/#CryptoUUIDUser CUserR GET !lecturerANDparticipant
|
/users/#CryptoUUIDUser CUserR GET POST !lecturerANDparticipant
|
||||||
/correctors CHiWisR GET
|
/correctors CHiWisR GET
|
||||||
/notes CNotesR GET POST !corrector
|
/notes CNotesR GET POST !corrector
|
||||||
/subs CCorrectionsR GET POST
|
/subs CCorrectionsR GET POST
|
||||||
|
|||||||
@ -54,13 +54,20 @@ all :: Foldable f =>
|
|||||||
all test = F.foldr (\needle acc -> acc E.&&. test needle) true
|
all test = F.foldr (\needle acc -> acc E.&&. test needle) true
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- Allow usage of Tuples as DbtRowKey, i.e. SqlIn instances for tuples
|
-- Allow usage of Tuples as DbtRowKey, i.e. SqlIn instances for tuples
|
||||||
$(sqlInTuples [2..16])
|
$(sqlInTuples [2..16])
|
||||||
|
|
||||||
|
-- | Example for usage of unValueN
|
||||||
|
_example_unValueN :: (E.Value a, E.Value b, E.Value c) -> (a,b,c)
|
||||||
|
_example_unValueN = $(unValueN 3)
|
||||||
|
|
||||||
|
-- | Example for usage of unValueNIs
|
||||||
|
_example_unValueNIs :: (E.Value a, b, E.Value c) -> (a,b,c)
|
||||||
|
_example_unValueNIs = $(unValueNIs 3 [1,3])
|
||||||
|
|
||||||
-- | Example for usage of sqlIJproj
|
-- | Example for usage of sqlIJproj
|
||||||
-- queryFeaturesDegree :: (a `E.InnerJoin` b `E.InnerJoin` c) -> b
|
_queryFeaturesDegree :: (a `E.InnerJoin` b `E.InnerJoin` c) -> b
|
||||||
-- queryFeaturesDegree = $(sqlIJproj 3 2)
|
_queryFeaturesDegree = $(sqlIJproj 3 2)
|
||||||
|
|
||||||
|
|
||||||
-- | generic filter creation for dbTable
|
-- | generic filter creation for dbTable
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
module Database.Esqueleto.Utils.TH
|
module Database.Esqueleto.Utils.TH
|
||||||
( SqlIn(..)
|
( SqlIn(..)
|
||||||
, sqlInTuple, sqlInTuples
|
, sqlInTuple, sqlInTuples
|
||||||
|
, unValueN, unValueNIs
|
||||||
, sqlIJproj, sqlLOJproj
|
, sqlIJproj, sqlLOJproj
|
||||||
) where
|
) where
|
||||||
|
|
||||||
@ -48,6 +49,30 @@ sqlInTuple arity = do
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
-- | Generic unValuing of Tuples of Values, i.e.
|
||||||
|
-- $(unValueN 3) :: (E.Value a, E.Value b, E.Value c) -> (a,b,c)
|
||||||
|
unValueN :: Int -> ExpQ
|
||||||
|
unValueN arity = do
|
||||||
|
vs <- replicateM arity $ newName "v"
|
||||||
|
let pat = tupP $ map varP vs
|
||||||
|
let uvE v = [e|E.unValue $(varE v)|]
|
||||||
|
let rhs = tupE $ map uvE vs
|
||||||
|
lam1E pat rhs
|
||||||
|
|
||||||
|
-- | Generic unValuing of certain indices of a Tuple, i.e.
|
||||||
|
-- $(unValueNIs 3 [1,3]) :: (E.Value a, b, E.Value c) -> (a,b,c)
|
||||||
|
unValueNIs :: Int -> [Int] -> ExpQ
|
||||||
|
unValueNIs arity uvIdx = do
|
||||||
|
vs <- replicateM arity $ newName "v"
|
||||||
|
let pat = tupP $ map varP vs
|
||||||
|
let rhs = tupE $ map uvEi $ zip vs [1..]
|
||||||
|
lam1E pat rhs
|
||||||
|
where
|
||||||
|
uvEi (v,i) | i `elem` uvIdx = [e|E.unValue $(varE v)|]
|
||||||
|
| otherwise = varE v
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- | Generic projections for InnerJoin-tuples
|
-- | Generic projections for InnerJoin-tuples
|
||||||
-- gives I-th element of N-tuple of left-associative InnerJoin-pairs,
|
-- gives I-th element of N-tuple of left-associative InnerJoin-pairs,
|
||||||
-- i.e. @$(projN n m) :: (t1 `E.InnerJoin` .. `E.InnerJoin` tn) -> tm@ (for m<=n)
|
-- i.e. @$(projN n m) :: (t1 `E.InnerJoin` .. `E.InnerJoin` tn) -> tm@ (for m<=n)
|
||||||
|
|||||||
@ -1147,7 +1147,7 @@ instance YesodBreadcrumbs UniWorX where
|
|||||||
-- (CourseR tid ssh csh CRegisterR) -- is POST only
|
-- (CourseR tid ssh csh CRegisterR) -- is POST only
|
||||||
breadcrumb (CourseR tid ssh csh CEditR) = return ("Editieren" , Just $ CourseR tid ssh csh CShowR)
|
breadcrumb (CourseR tid ssh csh CEditR) = return ("Editieren" , Just $ CourseR tid ssh csh CShowR)
|
||||||
breadcrumb (CourseR tid ssh csh CUsersR) = return ("Anmeldungen", Just $ CourseR tid ssh csh CShowR)
|
breadcrumb (CourseR tid ssh csh CUsersR) = return ("Anmeldungen", Just $ CourseR tid ssh csh CShowR)
|
||||||
breadcrumb (CourseR tid ssh csh (CUserR _)) = return ("Teilnehmer" , Just $ CourseR tid ssh csh CShowR)
|
breadcrumb (CourseR tid ssh csh (CUserR _)) = return ("Teilnehmer" , Just $ CourseR tid ssh csh CUsersR)
|
||||||
breadcrumb (CourseR tid ssh csh CCorrectionsR) = return ("Abgaben" , Just $ CourseR tid ssh csh CShowR)
|
breadcrumb (CourseR tid ssh csh CCorrectionsR) = return ("Abgaben" , Just $ CourseR tid ssh csh CShowR)
|
||||||
breadcrumb (CourseR tid ssh csh SheetListR) = return ("Übungen" , Just $ CourseR tid ssh csh CShowR)
|
breadcrumb (CourseR tid ssh csh SheetListR) = return ("Übungen" , Just $ CourseR tid ssh csh CShowR)
|
||||||
breadcrumb (CourseR tid ssh csh SheetNewR ) = return ("Neu", Just $ CourseR tid ssh csh SheetListR)
|
breadcrumb (CourseR tid ssh csh SheetNewR ) = return ("Neu", Just $ CourseR tid ssh csh SheetListR)
|
||||||
|
|||||||
@ -807,7 +807,7 @@ getCUsersR tid ssh csh = do
|
|||||||
, sortable (Just "registration") (i18nCell MsgRegisteredHeader) (dateCell . view _userTableRegistration)
|
, sortable (Just "registration") (i18nCell MsgRegisteredHeader) (dateCell . view _userTableRegistration)
|
||||||
, colUserComment tid ssh csh
|
, colUserComment tid ssh csh
|
||||||
]
|
]
|
||||||
psValidator = def
|
psValidator = def & defaultSortingByName
|
||||||
Entity cid course <- getBy404 $ TermSchoolCourseShort tid ssh csh
|
Entity cid course <- getBy404 $ TermSchoolCourseShort tid ssh csh
|
||||||
numParticipants <- count [CourseParticipantCourse ==. cid]
|
numParticipants <- count [CourseParticipantCourse ==. cid]
|
||||||
participantTable <- makeCourseUserTable cid colChoices psValidator
|
participantTable <- makeCourseUserTable cid colChoices psValidator
|
||||||
@ -819,9 +819,9 @@ getCUsersR tid ssh csh = do
|
|||||||
$(widgetFile "course-participants")
|
$(widgetFile "course-participants")
|
||||||
|
|
||||||
|
|
||||||
|
getCUserR, postCUserR :: TermId -> SchoolId -> CourseShorthand -> CryptoUUIDUser -> Handler Html
|
||||||
getCUserR :: TermId -> SchoolId -> CourseShorthand -> CryptoUUIDUser -> Handler Html
|
getCUserR = postCUserR
|
||||||
getCUserR _tid _ssh _csh uCId = do
|
postCUserR tid ssh csh uCId = do
|
||||||
-- Has authorization checks (OR):
|
-- Has authorization checks (OR):
|
||||||
--
|
--
|
||||||
-- - User is current member of course
|
-- - User is current member of course
|
||||||
@ -831,20 +831,72 @@ getCUserR _tid _ssh _csh uCId = do
|
|||||||
-- - User is corrector for course
|
-- - User is corrector for course
|
||||||
-- - User is a tutor for course
|
-- - User is a tutor for course
|
||||||
-- - User is a lecturer for course
|
-- - User is a lecturer for course
|
||||||
|
let currentRoute = CourseR tid ssh csh (CUserR uCId)
|
||||||
|
dozentId <- requireAuthId
|
||||||
uid <- decrypt uCId
|
uid <- decrypt uCId
|
||||||
User{..} <- runDB $ get404 uid
|
-- DB reads
|
||||||
|
(cid, User{..}, thisUniqueNote, noteText, noteEdits ) <- runDB $ do
|
||||||
|
cid <- getKeyBy404 $ TermSchoolCourseShort tid ssh csh
|
||||||
|
user <- get404 uid
|
||||||
|
let thisUniqueNote = UniqueCourseUserNote uid cid
|
||||||
|
mbNoteEnt <- getBy thisUniqueNote
|
||||||
|
(noteText,noteEdits) <- case mbNoteEnt of
|
||||||
|
Nothing -> return (Nothing,[])
|
||||||
|
(Just (Entity noteKey CourseUserNote{courseUserNoteNote})) -> do
|
||||||
|
noteEdits <- E.select $ E.from $ \(edit `E.InnerJoin` usr) -> do
|
||||||
|
E.on $ edit E.^. CourseUserNoteEditUser E.==. usr E.^. UserId
|
||||||
|
E.where_ $ edit E.^. CourseUserNoteEditNote E.==. E.val noteKey
|
||||||
|
E.orderBy [E.desc $ edit E.^. CourseUserNoteEditTime]
|
||||||
|
E.limit 1 -- more will be shown, if changed here
|
||||||
|
return (edit E.^. CourseUserNoteEditTime, usr E.^. UserEmail, usr E.^. UserDisplayName, usr E.^. UserSurname)
|
||||||
|
return (Just courseUserNoteNote, $(unValueN 4) <$> noteEdits)
|
||||||
|
return (cid,user,thisUniqueNote,noteText,noteEdits)
|
||||||
|
let editByWgt = [whamlet|
|
||||||
|
$forall (etime,_eemail,ename,_esurname) <- noteEdits
|
||||||
|
<br>
|
||||||
|
_{MsgLastEdit} ^{editedByW SelFormatDateTime etime ename}
|
||||||
|
|] -- _{MsgLastEdit} ^{formatTimeW SelFormatDateTime etime} ^{nameWidget ename esurname}
|
||||||
|
|
||||||
|
((noteRes, noteView), noteEnctype) <- runFormPost . identifyForm FIDcUserNote . renderAForm FormStandard $
|
||||||
|
(aopt (annotateField editByWgt textField) (fslpI MsgCourseUserNote "Text" & setTooltip MsgCourseUserNoteTooltip) $ Just noteText)
|
||||||
|
<* submitButton
|
||||||
|
formResult noteRes $ \mbNote -> (do
|
||||||
|
let note = foldMap id mbNote -- Maybe Text to maybe empty Text
|
||||||
|
now <- liftIO getCurrentTime
|
||||||
|
if null note
|
||||||
|
then do
|
||||||
|
runDB $ do
|
||||||
|
-- must delete all edits due to foreign key constraints, which does not make sense -> refactor!
|
||||||
|
maybeM (return ()) (\nk -> deleteWhere [CourseUserNoteEditNote ==. nk]) (getKeyBy thisUniqueNote)
|
||||||
|
deleteBy thisUniqueNote
|
||||||
|
addMessageI Info MsgCourseUserNoteDeleted
|
||||||
|
else do
|
||||||
|
runDB $ do
|
||||||
|
(Entity noteKey _) <- upsertBy thisUniqueNote (CourseUserNote cid uid note) [CourseUserNoteNote =. note]
|
||||||
|
void . insert $ CourseUserNoteEdit dozentId now noteKey
|
||||||
|
addMessageI Success MsgCourseUserNoteSaved
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- USE src/utils/Form.formResult
|
-- USE src/utils/Form.formResult
|
||||||
defaultLayout -- TODO
|
defaultLayout -- TODO
|
||||||
[whamlet|
|
[whamlet|
|
||||||
<p>^{nameWidget userDisplayName userSurname}
|
<h2>^{nameWidget userDisplayName userSurname}
|
||||||
|
#{mailtoHtml userEmail}
|
||||||
|
<section>
|
||||||
|
<a id="note-form">
|
||||||
|
<form method=post action=@{currentRoute}#note-form enctype=#{noteEnctype}>
|
||||||
|
^{noteView}
|
||||||
|]
|
|]
|
||||||
|
|
||||||
|
|
||||||
getCHiWisR :: TermId -> SchoolId -> CourseShorthand -> Handler Html
|
getCHiWisR :: TermId -> SchoolId -> CourseShorthand -> Handler Html
|
||||||
getCHiWisR = error "CHiWisR: Not implemented"
|
getCHiWisR = error "CHiWisR: Not implemented"
|
||||||
|
|
||||||
getCNotesR, postCNotesR :: TermId -> SchoolId -> CourseShorthand -> Handler Html
|
getCNotesR, postCNotesR :: TermId -> SchoolId -> CourseShorthand -> Handler Html
|
||||||
-- NOTE: The route getNotesR is abused for correctorORlecturer access rights!
|
-- NOTE: The route getNotesR is abused for correctorORlecturer access rights!
|
||||||
-- PROBLEM: Correctors usually don't know Participants by name (anonymous), maybe notes are not shared?
|
-- PROBLEM: Correctors usually don't know Participants by name (anonymous), maybe notes are not shared?
|
||||||
|
-- If they are shared, adjust MsgCourseUserNoteTooltip
|
||||||
getCNotesR = error "CNotesR: Not implemented"
|
getCNotesR = error "CNotesR: Not implemented"
|
||||||
postCNotesR = error "CNotesR: Not implemented"
|
postCNotesR = error "CNotesR: Not implemented"
|
||||||
|
|||||||
@ -130,7 +130,7 @@ getProfileDataR = do
|
|||||||
dataWidget
|
dataWidget
|
||||||
$(widgetFile "dsgvDisclaimer")
|
$(widgetFile "dsgvDisclaimer")
|
||||||
|
|
||||||
makeProfileData :: (Entity User) -> DB Widget
|
makeProfileData :: Entity User -> DB Widget
|
||||||
makeProfileData (Entity uid User{..}) = do
|
makeProfileData (Entity uid User{..}) = do
|
||||||
-- MsgRenderer mr <- getMsgRenderer
|
-- MsgRenderer mr <- getMsgRenderer
|
||||||
admin_rights <- E.select $ E.from $ \(adright `E.InnerJoin` school) -> do
|
admin_rights <- E.select $ E.from $ \(adright `E.InnerJoin` school) -> do
|
||||||
|
|||||||
@ -3,7 +3,6 @@ module Handler.SystemMessage where
|
|||||||
import Import
|
import Import
|
||||||
|
|
||||||
import qualified Data.Map.Lazy as Map
|
import qualified Data.Map.Lazy as Map
|
||||||
import qualified Data.Text as Text
|
|
||||||
|
|
||||||
import qualified Data.Set as Set
|
import qualified Data.Set as Set
|
||||||
|
|
||||||
@ -16,13 +15,7 @@ import Utils.Lens
|
|||||||
|
|
||||||
import qualified Database.Esqueleto as E
|
import qualified Database.Esqueleto as E
|
||||||
|
|
||||||
|
-- htmlField' moved to Handler.Utils.Form/Fields
|
||||||
htmlField' :: Field (HandlerT UniWorX IO) Html
|
|
||||||
htmlField' = htmlField
|
|
||||||
{ fieldParse = \vs fis -> fieldParse htmlField (map Text.strip vs) fis
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
getMessageR, postMessageR :: CryptoUUIDSystemMessage -> Handler Html
|
getMessageR, postMessageR :: CryptoUUIDSystemMessage -> Handler Html
|
||||||
getMessageR = postMessageR
|
getMessageR = postMessageR
|
||||||
|
|||||||
@ -91,6 +91,12 @@ wrapMailto (original -> email) linkText
|
|||||||
mailtoHtml :: CI Text -> Html
|
mailtoHtml :: CI Text -> Html
|
||||||
mailtoHtml email = wrapMailto email $ toHtml email
|
mailtoHtml email = wrapMailto email $ toHtml email
|
||||||
|
|
||||||
|
-- | Generic i18n text for "edited at sometime by someone"
|
||||||
|
editedByW :: SelDateTimeFormat -> UTCTime -> Text -> Widget
|
||||||
|
editedByW fmt tm usr = do
|
||||||
|
ft <- handlerToWidget $ formatTime fmt tm
|
||||||
|
[whamlet|_{MsgEditedBy usr ft}|]
|
||||||
|
|
||||||
-- | Prefix a message with a short course id,
|
-- | Prefix a message with a short course id,
|
||||||
-- eg. for window title bars, etc.
|
-- eg. for window title bars, etc.
|
||||||
-- This function should help to make this consistent everywhere
|
-- This function should help to make this consistent everywhere
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import Handler.Utils.DateTime
|
|||||||
|
|
||||||
import Import hiding (cons)
|
import Import hiding (cons)
|
||||||
import qualified Data.Char as Char
|
import qualified Data.Char as Char
|
||||||
|
import qualified Data.Text as Text
|
||||||
import qualified Data.CaseInsensitive as CI
|
import qualified Data.CaseInsensitive as CI
|
||||||
|
|
||||||
-- import Yesod.Core
|
-- import Yesod.Core
|
||||||
@ -141,6 +141,16 @@ buttonForm = identifyForm FIDbuttonForm buttonFormAux -- TODO: distinguish diffe
|
|||||||
-- Fields --
|
-- Fields --
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
-- | add some additional text immediately after the field widget; probably not a good idea to use
|
||||||
|
annotateField :: ToWidget (HandlerSite m) wgt => wgt -> Field m a -> Field m a
|
||||||
|
annotateField ann field@Field{fieldView=fvf} =
|
||||||
|
let fvf' idt nmt atts ei bl =
|
||||||
|
[whamlet|
|
||||||
|
^{fvf idt nmt atts ei bl}
|
||||||
|
^{ann}
|
||||||
|
|]
|
||||||
|
in field { fieldView=fvf'}
|
||||||
|
|
||||||
-- ciField moved to Utils.Form
|
-- ciField moved to Utils.Form
|
||||||
|
|
||||||
routeField :: ( Monad m
|
routeField :: ( Monad m
|
||||||
@ -148,6 +158,12 @@ routeField :: ( Monad m
|
|||||||
) => Field m (Route UniWorX)
|
) => Field m (Route UniWorX)
|
||||||
routeField = checkMMap (return . maybe (Left MsgInvalidRoute) Right . fromPathPiece) toPathPiece textField
|
routeField = checkMMap (return . maybe (Left MsgInvalidRoute) Right . fromPathPiece) toPathPiece textField
|
||||||
|
|
||||||
|
-- | Variant that simply removes leading and trailing white space
|
||||||
|
htmlField' :: Field (HandlerT UniWorX IO) Html
|
||||||
|
htmlField' = htmlField
|
||||||
|
{ fieldParse = \vs fis -> fieldParse htmlField (map Text.strip vs) fis
|
||||||
|
}
|
||||||
|
|
||||||
natFieldI :: (Monad m, Integral i, RenderMessage (HandlerSite m) msg, RenderMessage (HandlerSite m) FormMessage) => msg -> Field m i
|
natFieldI :: (Monad m, Integral i, RenderMessage (HandlerSite m) msg, RenderMessage (HandlerSite m) FormMessage) => msg -> Field m i
|
||||||
natFieldI msg = checkBool (>= 0) msg intField
|
natFieldI msg = checkBool (>= 0) msg intField
|
||||||
|
|
||||||
|
|||||||
@ -66,8 +66,8 @@ sortUserDisplayName queryUser = ("user-display-name", SortColumn $ queryUser >>>
|
|||||||
|
|
||||||
defaultSortingByName :: PSValidator m x -> PSValidator m x
|
defaultSortingByName :: PSValidator m x -> PSValidator m x
|
||||||
defaultSortingByName =
|
defaultSortingByName =
|
||||||
defaultSorting [SortAscBy "user-surname", SortAscBy "user-display-name"] -- old way, requiring two exta sorters
|
-- defaultSorting [SortAscBy "user-surname", SortAscBy "user-display-name"] -- old way, requiring two exta sorters
|
||||||
-- defaultSorting [SortAscBy "user-name"] -- new way, untested, working with single sorter
|
defaultSorting [SortAscBy "user-name"] -- new way, untested, working with single sorter
|
||||||
|
|
||||||
-- | Alias for sortUserName for consistency
|
-- | Alias for sortUserName for consistency
|
||||||
fltrUserNameLink :: (IsFilterColumn t (a -> Set Text -> E.SqlExpr (E.Value Bool)), IsString d) => (a -> E.SqlExpr (Entity User)) -> (d, FilterColumn t)
|
fltrUserNameLink :: (IsFilterColumn t (a -> Set Text -> E.SqlExpr (E.Value Bool)), IsString d) => (a -> E.SqlExpr (Entity User)) -> (d, FilterColumn t)
|
||||||
|
|||||||
@ -218,6 +218,7 @@ data FormIdentifier
|
|||||||
| FIDDelete
|
| FIDDelete
|
||||||
| FIDCourseRegister
|
| FIDCourseRegister
|
||||||
| FIDuserRights
|
| FIDuserRights
|
||||||
|
| FIDcUserNote
|
||||||
| FIDbuttonForm
|
| FIDbuttonForm
|
||||||
deriving (Eq, Ord, Read, Show)
|
deriving (Eq, Ord, Read, Show)
|
||||||
|
|
||||||
|
|||||||
@ -24,7 +24,7 @@ projNI n i = do
|
|||||||
x <- newName "x"
|
x <- newName "x"
|
||||||
let rhs = varE x
|
let rhs = varE x
|
||||||
let pat = tupP $ replicate (pred i) wildP ++ varP x : replicate (n-i) wildP
|
let pat = tupP $ replicate (pred i) wildP ++ varP x : replicate (n-i) wildP
|
||||||
lamE [pat] rhs
|
lam1E pat rhs
|
||||||
|
|
||||||
|
|
||||||
-- | Generic projections N-tuples that are actually left-associative pairs
|
-- | Generic projections N-tuples that are actually left-associative pairs
|
||||||
@ -83,6 +83,14 @@ uncurryN n = do
|
|||||||
return $ LamE pat rhs
|
return $ LamE pat rhs
|
||||||
|
|
||||||
|
|
||||||
|
afterN :: Int -> ExpQ -- apply a function after another of arity N, i.e. $(afterN 1) = (.)
|
||||||
|
afterN n = do
|
||||||
|
f <- newName "f"
|
||||||
|
g <- newName "g"
|
||||||
|
--let rhs = [|$(curryN n) (g . ($(uncurryN n) f))|]
|
||||||
|
lamE [(varP g),(varP f)] [|$(curryN n) ($(varE g) . ($(uncurryN n) $(varE f)))|]
|
||||||
|
|
||||||
|
|
||||||
-- Special Show-Instances for Themes
|
-- Special Show-Instances for Themes
|
||||||
deriveShowWith :: (String -> String) -> Name -> Q [Dec]
|
deriveShowWith :: (String -> String) -> Name -> Q [Dec]
|
||||||
deriveShowWith = deriveSimpleWith ''Show 'show
|
deriveShowWith = deriveSimpleWith ''Show 'show
|
||||||
|
|||||||
Reference in New Issue
Block a user