Sheet Download works

This commit is contained in:
SJost 2018-03-16 10:26:54 +01:00
parent 85f132295c
commit c35f718054
7 changed files with 107 additions and 46 deletions

View File

@ -1,5 +1,8 @@
** i18n: ** i18n:
- i18n der Links, Page Titles und Buttons? - i18n der
Links ->
Page Titles -> setTitleI
Buttons?
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?
Was ist mit PageTitles, z.B. in Handler.Term.termEditHandler: Was ist mit PageTitles, z.B. in Handler.Term.termEditHandler:
-- setTitle [whamlet| _{MsgTermNewTitle} |] -- TODO, does not work -- setTitle [whamlet| _{MsgTermNewTitle} |] -- TODO, does not work
@ -10,9 +13,6 @@
** FORMS ** FORMS
1- Handler.Utils.Form.FormIdentifier: Still needed?
2- Verification of Ownership during Edit?
D.h. wo wird geprüft, dass Sheet Ersteller Lecturer im Kurs ist?
3 - Sheets: Multiple Files 3 - Sheets: Multiple Files
- Versionen für Studenten/Korrektoren/Lecturers/Admins?! - Versionen für Studenten/Korrektoren/Lecturers/Admins?!

1
routes
View File

@ -22,6 +22,7 @@
/course/#TermId/#Text/sheet/#Text/show SheetShowR GET /course/#TermId/#Text/sheet/#Text/show SheetShowR GET
/course/#TermId/#Text/sheet/#Text/#SheetFileType/#FilePath SheetFileR GET /course/#TermId/#Text/sheet/#Text/#SheetFileType/#FilePath SheetFileR GET
/course/#TermId/#Text/sheet/new SheetNewR GET POST /course/#TermId/#Text/sheet/new SheetNewR GET POST
-- TODO: Change routes to #Text statt #SheetId
/course/#TermId/#Text/sheet/#SheetId/edit SheetEditR GET POST /course/#TermId/#Text/sheet/#SheetId/edit SheetEditR GET POST
/course/#TermId/#Text/sheet/#SheetId/delete SheetDelR GET POST /course/#TermId/#Text/sheet/#SheetId/delete SheetDelR GET POST

View File

@ -200,6 +200,9 @@ 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 (CourseEditR t c) _ = courseLecturerAccess . entityKey =<< getBy404 (CourseTermShort t c)
isAuthorizedDB (SheetListR t c) False = return Authorized --
isAuthorizedDB (SheetShowR t c s) _ = return Authorized -- TODO: nur für angemeldete Kursteilnehmer falls sichtbar, sonst nur Lectrurer oder Korrektor
isAuthorizedDB (SheetFileR t c 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 (SheetListR t c) _ = courseLecturerAccess . entityKey =<< getBy404 (CourseTermShort t c)
isAuthorizedDB (SheetNewR t c) _ = courseLecturerAccess . entityKey =<< getBy404 (CourseTermShort t c) isAuthorizedDB (SheetNewR t c) _ = courseLecturerAccess . entityKey =<< getBy404 (CourseTermShort t c)
isAuthorizedDB (SheetEditR t c s) _ = courseLecturerAccess . entityKey =<< getBy404 (CourseTermShort t c) isAuthorizedDB (SheetEditR t c s) _ = courseLecturerAccess . entityKey =<< getBy404 (CourseTermShort t c)

View File

@ -48,7 +48,7 @@ data SheetForm = SheetForm
, sfHintF :: Maybe FileInfo , sfHintF :: Maybe FileInfo
, sfSolutionFrom :: Maybe UTCTime , sfSolutionFrom :: Maybe UTCTime
, sfSolutionF :: Maybe FileInfo , sfSolutionF :: Maybe FileInfo
, sfSheetId :: Maybe SheetId -- Keine SheetId im Formular!
} }
@ -57,11 +57,10 @@ makeSheetForm cid template = identForm FIDsheet $ \html -> do
-- TODO: Yesod.Form.MassInput.inputList arbeitet Server-seitig :( -- TODO: Yesod.Form.MassInput.inputList arbeitet Server-seitig :(
-- Erstmal nur mit ZIP arbeiten -- Erstmal nur mit ZIP arbeiten
(result, widget) <- flip (renderAForm FormStandard) html $ SheetForm (result, widget) <- flip (renderAForm FormStandard) html $ SheetForm
<$> areq textField (fsb "Name") (sfName <$> template) <$> areq textField (fsb "Name") (sfName <$> template)
<*> aopt htmlField (fsb "Hinweise für Teilnehmer") (sfMarkingText <$> template) <*> aopt htmlField (fsb "Hinweise für Teilnehmer") (sfMarkingText <$> template)
<*> sheetTypeAFormReq (fsb "Bewertung") (sfType <$> template) <*> sheetTypeAFormReq (fsb "Bewertung") (sfType <$> template)
<*> sheetGroupAFormReq (fsb "Abgabegruppengröße") (sfGrouping <$> template) <*> sheetGroupAFormReq (fsb "Abgabegruppengröße") (sfGrouping <$> template)
--TODO: SICHTBARKEIT hinzunehmen
<*> aopt htmlField (fsb "Hinweise für Korrektoren") (sfMarkingText <$> template) <*> aopt htmlField (fsb "Hinweise für Korrektoren") (sfMarkingText <$> template)
<*> aopt utcTimeField (fsb "Sichtbar ab") (sfVisibleFrom <$> template) <*> aopt utcTimeField (fsb "Sichtbar ab") (sfVisibleFrom <$> template)
<*> areq utcTimeField (fsb "Abgabe ab") (sfActiveFrom <$> template) <*> areq utcTimeField (fsb "Abgabe ab") (sfActiveFrom <$> template)
@ -71,7 +70,6 @@ makeSheetForm cid template = identForm FIDsheet $ \html -> do
<*> fileAFormOpt (fsb "Hinweis") <*> fileAFormOpt (fsb "Hinweis")
<*> aopt utcTimeField (fsb "Lösung ab") (sfSolutionFrom <$> template) <*> aopt utcTimeField (fsb "Lösung ab") (sfSolutionFrom <$> template)
<*> fileAFormOpt (fsb "Lösung") <*> fileAFormOpt (fsb "Lösung")
<*> aopt hiddenField "EditSheetId" (sfSheetId <$> template)
<* submitButton <* submitButton
return $ case result of return $ case result of
FormSuccess sheetResult FormSuccess sheetResult
@ -156,29 +154,27 @@ getSheetList courseEnt = do
-- Show single sheet -- Show single sheet
getSheetShowR :: TermId -> Text -> Text -> Handler Html getSheetShowR :: TermId -> Text -> Text -> Handler Html
getSheetShowR tid csh shn = getSheetShow =<< getSheetShowR tid csh shn = do
(runDB $ fetchSheet tid csh shn) entSheet <- runDB $ fetchSheet tid csh shn
{- Nur per UUID
getSheetIdShowR :: SheetId -> Handler Html
getSheetIdShowR sheetId = getSheetShow =<<
(Entity sheetId) <$> (runDB $ get404 sheetId)\
-}{-
getSheetUUIDShowR :: CryptoUUIDSheet -> Handler Html
getSheetUUIDShowR sUUID = do
cIDKey <- getsYesod appCryptoIDKey
sheetId <- UUID.decrypt cIDKey sUUID
sheetEnt <- runDB $ get404 sheetId
getSheetShow $ Entity sheetId sheetEnt
-}
getSheetShow :: (Entity Sheet) -> Handler Html
getSheetShow entSheet = do
let sheet = entityVal entSheet let sheet = entityVal entSheet
sid = entityKey entSheet
--
fileNameTypes <- runDB $ E.select $ E.from $
\(sheet `E.InnerJoin` sheetFile `E.InnerJoin` file) -> do
-- Restrict to consistent rows that correspond to each other
E.on (file E.^. FileId E.==. sheetFile E.^. SheetFileFileId)
E.on (sheetFile E.^. SheetFileSheetId E.==. sheet E.^. SheetId)
-- filter to requested file
E.where_ (sheet E.^. SheetId E.==. E.val sid )
-- return desired columns
return $ (file E.^. FileTitle, sheetFile E.^. SheetFileType)
let fileLinks = map (\(E.Value fName, E.Value fType) -> SheetFileR tid csh shn fType fName) fileNameTypes
defaultLayout $ do defaultLayout $ do
setTitle $ toHtml $ T.append "Übung " $ sheetName sheet setTitle $ toHtml $ T.append "Übung " $ sheetName sheet
[whamlet| Under Construction !!! |] -- TODO
$(widgetFile "sheetAdmin") $(widgetFile "sheetAdmin")
[whamlet| Under Construction !!! |] -- TODO
getSheetFileR :: TermId -> Text -> Text -> SheetFileType -> FilePath -> Handler TypedContent getSheetFileR :: TermId -> Text -> Text -> SheetFileType -> FilePath -> Handler TypedContent
getSheetFileR tid csh shn typ title = do getSheetFileR tid csh shn typ title = do
@ -209,7 +205,6 @@ getSheetNewR :: TermId -> Text -> Handler Html
getSheetNewR tid csh = do getSheetNewR tid csh = do
let tident = unTermKey tid let tident = unTermKey tid
aid <- requireAuthId aid <- requireAuthId
-- TODO: Verify that aid is lecturer in Course? Here or in Auth?
(Entity cid course) <- runDB $ getBy404 $ CourseTermShort tid csh (Entity cid course) <- runDB $ getBy404 $ CourseTermShort tid csh
let template = Nothing -- TODO: provide convenience by interpolating name/nr/dates+7days let template = Nothing -- TODO: provide convenience by interpolating name/nr/dates+7days
((res,wdgt), enc) <- runFormPost $ makeSheetForm cid template ((res,wdgt), enc) <- runFormPost $ makeSheetForm cid template
@ -233,20 +228,33 @@ getSheetNewR tid csh = do
, sheetCreatedBy = aid , sheetCreatedBy = aid
, sheetChangedBy = aid , sheetChangedBy = aid
} }
insertOkay <- runDB $ insertUnique sheet saveOkay <- runDB $ do
case insertOkay of insertOkay <- insertUnique sheet
Nothing -> addMessageI "danger" $ MsgSheetNewDup tident csh sfName case insertOkay of
(Just sid) -> do Nothing -> insertOkay <$ addMessageI "danger" (MsgSheetNewDup tident csh sfName)
addMessageI "info" $ MsgSheetNewOk tident csh sfName (Just sid) -> do
-- Save Files in DB: addMessageI "info" $ MsgSheetNewOk tident csh sfName
-- Prüfe, das FileTitle innerhalb des Sheets eindeutig ist für diesen SheetFileTpye -- Save Files in DB:
whenIsJust sfSheetF $ \sinfo -> do -- Prüfe, das FileTitle innerhalb des Sheets eindeutig ist für diesen SheetFileTpye
let sheetInsert file = do whenIsJust sfSheetF $ \sinfo -> do
fid <- insert file let sheetInsert file = do
void . insert $ SheetFile sid fid SheetExercise -- Uniqueness? fid <- insert file
runDB . runConduit $ (sourceFiles sinfo) =$= C.mapM_ sheetInsert void . insert $ SheetFile sid fid SheetExercise -- cannot fail due to uniqueness, since we generated a fresh FileId in the previous step
runConduit $ (sourceFiles sinfo) =$= C.mapM_ sheetInsert
redirect $ SheetShowR tid csh sfName --
whenIsJust sfHintF $ \sinfo -> do
let sheetInsert file = do
fid <- insert file
void . insert $ SheetFile sid fid SheetHint -- cannot fail due to uniqueness, since we generated a fresh FileId in the previous step
runConduit $ (sourceFiles sinfo) =$= C.mapM_ sheetInsert
--
whenIsJust sfSolutionF $ \sinfo -> do
let sheetInsert file = do
fid <- insert file
void . insert $ SheetFile sid fid SheetSolution -- cannot fail due to uniqueness, since we generated a fresh FileId in the previous step
runConduit $ (sourceFiles sinfo) =$= C.mapM_ sheetInsert
return insertOkay
when (isJust saveOkay) $ redirect $ SheetShowR tid csh sfName
(FormFailure msgs) -> forM_ msgs $ (addMessage "warning") . toHtml (FormFailure msgs) -> forM_ msgs $ (addMessage "warning") . toHtml
_ -> return () _ -> return ()
defaultLayout $ do defaultLayout $ do

View File

@ -1,12 +1,15 @@
{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeFamilies, FlexibleContexts, ConstraintKinds #-}
{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE QuasiQuotes #-}
module Handler.Utils module Handler.Utils
( module Handler.Utils ( module Handler.Utils
) where ) where
import Import.NoFoundation import Import.NoFoundation
import Handler.Utils.DateTime as Handler.Utils import Handler.Utils.DateTime as Handler.Utils
@ -22,6 +25,9 @@ import Text.Blaze (Markup, ToMarkup)
import Data.Map (Map) import Data.Map (Map)
import qualified Data.Map as Map import qualified Data.Map as Map
import qualified Data.List as List
import Database.Persist.Class
tickmark :: IsString a => a tickmark :: IsString a => a
tickmark = fromString "" tickmark = fromString ""
@ -61,3 +67,38 @@ whenIsJust Nothing _ = return ()
entities2map :: PersistEntity record => [Entity record] -> Map (Key record) record entities2map :: PersistEntity record => [Entity record] -> Map (Key record) record
entities2map = foldl' (\m entity -> Map.insert (entityKey entity) (entityVal entity) m) Map.empty entities2map = foldl' (\m entity -> Map.insert (entityKey entity) (entityVal entity) m) Map.empty
--------
-- DB --
--------
myReplaceUnique
:: (MonadIO m
,Eq (Unique record)
,PersistRecordBackend record backend
,PersistUniqueWrite backend)
=> Key record -> record -> ReaderT backend m (Maybe (Unique record))
myReplaceUnique key datumNew = getJust key >>= replaceOriginal
where
uniqueKeysNew = persistUniqueKeys datumNew
replaceOriginal original = do
conflict <- checkUniqueKeys changedKeys
case conflict of
Nothing -> replace key datumNew >> return Nothing
(Just conflictingKey) -> return $ Just conflictingKey
where
changedKeys = uniqueKeysNew List.\\ uniqueKeysOriginal
uniqueKeysOriginal = persistUniqueKeys original
checkUniqueKeys
:: (MonadIO m
,PersistEntity record
,PersistUniqueRead backend
,PersistRecordBackend record backend)
=> [Unique record] -> ReaderT backend m (Maybe (Unique record))
checkUniqueKeys [] = return Nothing
checkUniqueKeys (x:xs) = do
y <- getBy x
case y of
Nothing -> checkUniqueKeys xs
Just _ -> return (Just x)

View File

@ -35,9 +35,14 @@ data FormIdentifier = FIDcourse | FIDsheet
deriving (Enum, Eq, Ord, Bounded, Read, Show) deriving (Enum, Eq, Ord, Bounded, Read, Show)
identForm :: FormIdentifier -> Form a -> Form a -- TODO: Still needed? identForm :: FormIdentifier -> Form a -> Form a
identForm fid = identifyForm (T.pack $ show fid) identForm fid = identifyForm (T.pack $ show fid)
{- Hinweise zur Erinnerung:
- identForm primär, wenn es mehr als ein Formular pro Handler gibt
- nur einmal pro makeForm reicht
-}
------------------- -------------------
-- Form Renderer -- -- Form Renderer --
------------------- -------------------

View File

@ -24,7 +24,10 @@
<div .row> <div .row>
<div .col-lg-12> <div .col-lg-12>
<h2>Abgaben <h2>Dateien
<ul>
$forall fileLink <- fileLinks
<li> <a href=@{fileLink}>@{fileLink}
<hr> <hr>