Saving material mostly implemented

This commit is contained in:
Steffen Jost 2019-04-29 18:22:07 +02:00
parent 212533d88e
commit 22ffa3477d
8 changed files with 80 additions and 28 deletions

View File

@ -142,8 +142,8 @@ SheetNewOk tid@TermId ssh@SchoolId csh@CourseShorthand sheetName@SheetNa
SheetTitle tid@TermId ssh@SchoolId csh@CourseShorthand sheetName@SheetName: #{display tid}-#{display ssh}-#{csh} #{sheetName}
SheetTitleNew tid@TermId ssh@SchoolId csh@CourseShorthand : #{display tid}-#{display ssh}-#{csh}: Neues Übungsblatt
SheetEditHead tid@TermId ssh@SchoolId csh@CourseShorthand sheetName@SheetName: #{display tid}-#{display ssh}-#{csh} #{sheetName} editieren
SheetEditOk tid@TermId ssh@SchoolId csh@CourseShorthand sheetName@SheetName: Übungsblatt #{sheetName} aus Kurs #{display tid}-#{display ssh}-#{csh} wurde gespeichert.
SheetNameDup tid@TermId ssh@SchoolId csh@CourseShorthand sheetName@SheetName: Es gibt bereits ein Übungsblatt #{sheetName} in diesem Kurs #{display tid}-#{display ssh}-#{csh}.
SheetEditOk tid@TermId ssh@SchoolId csh@CourseShorthand sheetName@SheetName: Übungsblatt #{sheetName} wurde gespeichert in Kurs #{display tid}-#{display ssh}-#{csh}
SheetNameDup tid@TermId ssh@SchoolId csh@CourseShorthand sheetName@SheetName: Es gibt bereits ein Übungsblatt #{sheetName} in diesem Kurs #{display tid}-#{display ssh}-#{csh}
SheetDelHead tid@TermId ssh@SchoolId csh@CourseShorthand sheetName@SheetName: #{sheetName} wirklich aus Kurs #{display tid}-#{display ssh}-#{csh} herauslöschen? Alle assoziierten Abgaben und Korrekturen gehen ebenfalls verloren!
SheetDelOk tid@TermId ssh@SchoolId csh@CourseShorthand sheetName@SheetName: #{display tid}-#{display ssh}-#{csh}: #{sheetName} gelöscht.
SheetDelHasSubmissions objs@Int: Inkl. #{tshow objs} #{pluralDE objs "Abgabe" "Abgaben"}!
@ -220,8 +220,10 @@ MaterialVisibleFromTip: Ohne Datum nie sichtbar für Teilnehmer; leer lassen ist
MaterialFiles: Dateien
MaterialNewHeading: Neues Material veröffentlichen
MaterialNewTitle: Neues Material
MaterialEditHeading name@Text: Material "#{name}" editieren
MaterialEditTitle name@Text: Material "#{name}" editieren
MaterialEditHeading materialName@MaterialName: Material "#{materialName}" editieren
MaterialEditTitle materialName@MaterialName: Material "#{materialName}" editieren
MaterialSaveOk tid@TermId ssh@SchoolId csh@CourseShorthand materialName@MaterialName: Material "#{materialName}" erfolgreich gespeichert in Kurs #{display tid}-#{display ssh}-#{csh}
MaterialNameDup tid@TermId ssh@SchoolId csh@CourseShorthand materialName@MaterialName: Es gibt bereits Material mit Namen "#{materialName}" in diesem Kurs #{display tid}-#{display ssh}-#{csh}
Unauthorized: Sie haben hierfür keine explizite Berechtigung.

View File

@ -6,6 +6,7 @@ Material -- course material for disemination to course participants
visibleFrom UTCTime Maybe -- Invisible to enrolled participants before
lastEdit UTCTime
UniqueMaterial course name
deriving Generic
MaterialFile -- a file that is part of a material distribution
material MaterialId
file FileId

View File

@ -5,7 +5,7 @@ import Import
import Data.Set (Set)
import qualified Data.Set as Set
import qualified Data.Conduit.List as C
import qualified Data.CaseInsensitive as CI
-- import qualified Data.CaseInsensitive as CI
import qualified Database.Esqueleto as E
@ -86,9 +86,9 @@ postMEditR tid ssh csh mnm = do
, mfVisibleFrom = materialVisibleFrom
, mfFiles = Just $ yieldMany files
}
editWidget <- handleMaterialEdit tid ssh csh template
let headingLong = prependCourseTitle tid ssh csh $ MsgMaterialEditHeading $ CI.original mnm
headingShort = prependCourseTitle tid ssh csh $ MsgMaterialEditTitle $ CI.original mnm
editWidget <- handleMaterialEdit tid ssh csh cid template $ uniqueReplace mid
let headingLong = prependCourseTitle tid ssh csh $ MsgMaterialEditHeading mnm
headingShort = prependCourseTitle tid ssh csh $ MsgMaterialEditTitle mnm
siteLayoutMsg headingLong $ do
setTitleI headingShort
editWidget
@ -97,23 +97,50 @@ postMEditR tid ssh csh mnm = do
getMaterialNewR, postMaterialNewR :: TermId -> SchoolId -> CourseShorthand -> Handler Html
getMaterialNewR = postMaterialNewR
postMaterialNewR tid ssh csh = do
editWidget <- handleMaterialEdit tid ssh csh Nothing
Entity cid _ <- runDB . getBy404 $ TermSchoolCourseShort tid ssh csh
editWidget <- handleMaterialEdit tid ssh csh cid Nothing insertUnique
let headingLong = prependCourseTitle tid ssh csh MsgMaterialNewHeading
headingShort = prependCourseTitle tid ssh csh MsgMaterialNewTitle
siteLayoutMsg headingLong $ do
setTitleI headingShort
editWidget
handleMaterialEdit :: TermId -> SchoolId -> CourseShorthand -> Maybe MaterialForm -> Handler Widget
handleMaterialEdit tid ssh csh template = do
aid <- requireAuthId
Entity cid _ <- runDB . getBy404 $ TermSchoolCourseShort tid ssh csh
((res,formWidget), formEnctype) <- runFormPost $ makeMaterialForm cid template
actionUrl <- fromMaybe (CourseR tid ssh csh MaterialNewR) <$> getCurrentRoute
return $ wrapForm formWidget def
{ formAction = Just $ SomeRoute actionUrl
, formEncoding = formEnctype
}
handleMaterialEdit :: TermId -> SchoolId -> CourseShorthand -> CourseId -> Maybe MaterialForm -> (Material -> DB (Maybe MaterialId)) -> Handler Widget
handleMaterialEdit tid ssh csh cid template dbMaterial = do
((res,formWidget), formEnctype) <- runFormPost $ makeMaterialForm cid template
formResult res saveMaterial
-- actionUrl <- fromMaybe (CourseR tid ssh csh MaterialNewR) <$> getCurrentRoute
return $ wrapForm formWidget def
{ formAction = Nothing -- Just $ SomeRoute actionUrl
, formEncoding = formEnctype
}
where
saveMaterial :: MaterialForm -> Handler ()
saveMaterial MaterialForm{..} = do
_aid <- requireAuthId
now <- liftIO getCurrentTime
let newMaterial = Material
{ materialCourse = cid
, materialName = mfName
, materialType = mfType
, materialDescription = mfDescription
, materialVisibleFrom = mfVisibleFrom
, materialLastEdit = now
}
saveOk <- runDB $ do
mbmid <- dbMaterial newMaterial
case mbmid of
Nothing -> False <$ addMessageI Error (MsgMaterialNameDup tid ssh csh mfName)
(Just mid) -> do -- save files in DB
whenIsJust mfFiles $ insertMaterialFile' mid
addMessageI Success $ MsgMaterialSaveOk tid ssh csh mfName
-- more info/warnings could go here
return True
when saveOk $ redirect -- redirect must happen outside of runDB
$ CourseR tid ssh csh (MaterialR mfName MShowR)
insertMaterialFile' :: MaterialId -> Source Handler (Either FileId File) -> DB ()
insertMaterialFile' = error "TODO"
getMDelR, postMDelR :: TermId -> SchoolId -> CourseShorthand -> MaterialName -> Handler Html

View File

@ -503,11 +503,7 @@ getSEditR tid ssh csh shn = do
, sfMarkingF = Just . yieldMany . map Left . Set.elems $ sheetFileIds SheetMarking
, sfMarkingText = sheetMarkingText
}
let action newSheet = do
replaceRes <- myReplaceUnique sid $ newSheet
case replaceRes of
Nothing -> return $ Just sid
(Just _err) -> return $ Nothing -- More specific error message for edit old sheet could go here
let action = uniqueReplace sid -- More specific error message for edit old sheet could go here by using myReplaceUnique instead
handleSheetEdit tid ssh csh (Just sid) template action
postSEditR :: TermId -> SchoolId -> CourseShorthand -> SheetName -> Handler Html

View File

@ -33,8 +33,9 @@ share [mkPersist sqlSettings, mkDeleteCascade sqlSettings, mkMigrate "migrateAll
$(persistDirectoryWith lowerCaseSettings "models")
-- (Eq Course) is impossible so we derive it for the Uniqueness Constraint only
deriving instance Eq (Unique Course)
deriving instance Eq (Unique Sheet)
deriving instance Eq (Unique Course) -- instance Eq TermSchoolCourseShort; instance Eq TermSchoolCourseName
deriving instance Eq (Unique Sheet) -- instance Eq CourseSheet
deriving instance Eq (Unique Material) -- instance Eq UniqueMaterial
-- Primary keys mentioned in dbtable row-keys must be Binary
-- Automatically generated (i.e. numeric) ids are already taken care of

View File

@ -405,6 +405,12 @@ toNothing = const Nothing
toNothingS :: String -> Maybe b
toNothingS = const Nothing
-- MOVED TO UTILS.DB due to cyclic dependency
-- Swap 'Nothing' for 'Just' and vice versa
-- flipMaybe :: b -> Maybe b -> Maybe b
-- flipMaybe x Nothing = Just x
-- flipMaybe _ (Just _) = Nothing
maybeAdd :: Num a => Maybe a -> Maybe a -> Maybe a -- treats Nothing as neutral/zero, unlike fmap/ap
maybeAdd (Just x) (Just y) = Just (x + y)
maybeAdd Nothing y = y

View File

@ -10,6 +10,16 @@ import qualified Data.Set as Set
import qualified Database.Esqueleto as E
-- import Database.Persist -- currently not needed here
-- | Swap 'Nothing' for 'Just' and vice versa
-- This belongs into Module 'Utils' but we have a weird cyclic
-- dependency
flipMaybe :: b -> Maybe a -> Maybe b
flipMaybe x Nothing = Just x
flipMaybe _ (Just _) = Nothing
emptyOrIn :: PersistField typ =>
E.SqlExpr (E.Value typ) -> Set typ -> E.SqlExpr (E.Value Bool)
emptyOrIn criterion testSet
@ -41,7 +51,16 @@ updateBy uniq updates = do
key <- getKeyBy uniq
for_ key $ flip update updates
myReplaceUnique -- Identical to Database.Persist.Class, except for the better type signature (original requires Eq record which is not needed anyway)
-- | Like 'myReplaceUnique' or 'replaceUnique' but with reversed result: returns 'Nothing' if the replacement was not possible,
-- and 'Just key' for the successfully replaced record
uniqueReplace :: (MonadIO m
,Eq (Unique record)
,PersistRecordBackend record backend
,PersistUniqueWrite backend)
=> Key record -> record -> ReaderT backend m (Maybe (Key record))
uniqueReplace key datumNew = flipMaybe key <$> myReplaceUnique key datumNew
myReplaceUnique -- | Identical to 'Database.Persist.Class', except for the better type signature (original requires Eq record which is not needed anyway)
:: (MonadIO m
,Eq (Unique record)
,PersistRecordBackend record backend

View File

@ -572,7 +572,7 @@ formFailure errs' = do
mr <- getMessageRender
return . FormFailure $ map mr errs'
-- | Turns errors into alerts, ignores missing forms and applies processing function
formResult :: MonadHandler m => FormResult a -> (a -> m ()) -> m ()
formResult res f = void . formResultMaybe res $ \x -> Nothing <$ f x