Conflicts: yesod-auth/Yesod/Auth.hs yesod-auth/Yesod/Auth/BrowserId.hs yesod-auth/Yesod/Auth/Dummy.hs yesod-auth/Yesod/Auth/Email.hs yesod-auth/Yesod/Auth/HashDB.hs yesod-auth/Yesod/Auth/OpenId.hs yesod-auth/Yesod/Auth/Rpxnow.hs yesod-form/Yesod/Form/Fields.hs yesod-form/Yesod/Form/Functions.hs yesod-form/Yesod/Form/Jquery.hs yesod-form/Yesod/Form/Nic.hs yesod-form/Yesod/Helpers/Crud.hs yesod-newsfeed/Yesod/AtomFeed.hs yesod-newsfeed/Yesod/RssFeed.hs
183 lines
6.2 KiB
Haskell
183 lines
6.2 KiB
Haskell
{-# LANGUAGE TypeFamilies, QuasiQuotes, TemplateHaskell #-}
|
|
{-# LANGUAGE FlexibleContexts #-}
|
|
{-# LANGUAGE FlexibleInstances #-}
|
|
{-# LANGUAGE MultiParamTypeClasses #-}
|
|
{-# LANGUAGE Rank2Types #-}
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
module Yesod.Helpers.Crud
|
|
( Item (..)
|
|
, Crud (..)
|
|
, CrudRoute (..)
|
|
, defaultCrud
|
|
) where
|
|
|
|
import Yesod.Core
|
|
import Text.Hamlet
|
|
import Yesod.Form
|
|
import Language.Haskell.TH.Syntax
|
|
import Yesod.Persist
|
|
import Data.Text (Text)
|
|
import Web.Routes.Quasi (toSinglePiece, fromSinglePiece)
|
|
|
|
-- | An entity which can be displayed by the Crud subsite.
|
|
class Item a where
|
|
-- | The title of an entity, to be displayed in the list of all entities.
|
|
itemTitle :: a -> Text
|
|
|
|
-- | Defines all of the CRUD operations (Create, Read, Update, Delete)
|
|
-- necessary to implement this subsite. When using the "Yesod.Form" module and
|
|
-- 'ToForm' typeclass, you can probably just use 'defaultCrud'.
|
|
data Crud master item = Crud
|
|
{ crudSelect :: GHandler (Crud master item) master [(Key item, item)]
|
|
, crudReplace :: Key item -> item -> GHandler (Crud master item) master ()
|
|
, crudInsert :: item -> GHandler (Crud master item) master (Key item)
|
|
, crudGet :: Key item -> GHandler (Crud master item) master (Maybe item)
|
|
, crudDelete :: Key item -> GHandler (Crud master item) master ()
|
|
}
|
|
|
|
mkYesodSub "Crud master item"
|
|
[ ClassP ''Item [VarT $ mkName "item"]
|
|
, ClassP ''SinglePiece [ConT ''Key `AppT` VarT (mkName "item")]
|
|
, ClassP ''ToForm [VarT $ mkName "item", VarT $ mkName "master"]
|
|
] [parseRoutes|
|
|
/ CrudListR GET
|
|
/add CrudAddR GET POST
|
|
/edit/#Text CrudEditR GET POST
|
|
/delete/#Text CrudDeleteR GET POST
|
|
|]
|
|
|
|
getCrudListR :: (Yesod master, Item item, SinglePiece (Key item))
|
|
=> GHandler (Crud master item) master RepHtml
|
|
getCrudListR = do
|
|
items <- getYesodSub >>= crudSelect
|
|
toMaster <- getRouteToMaster
|
|
defaultLayout $ do
|
|
setTitle "Items"
|
|
addWidget [hamlet|
|
|
<h1>Items
|
|
<ul>
|
|
$forall item <- items
|
|
<li>
|
|
<a href="@{toMaster (CrudEditR (toSinglePiece (fst item)))}">
|
|
\#{itemTitle (snd item)}
|
|
<p>
|
|
<a href="@{toMaster CrudAddR}">Add new item
|
|
|]
|
|
|
|
getCrudAddR :: (Yesod master, Item item, SinglePiece (Key item),
|
|
ToForm item master)
|
|
=> GHandler (Crud master item) master RepHtml
|
|
getCrudAddR = crudHelper
|
|
"Add new"
|
|
(Nothing :: Maybe (Key item, item))
|
|
False
|
|
|
|
postCrudAddR :: (Yesod master, Item item, SinglePiece (Key item),
|
|
ToForm item master)
|
|
=> GHandler (Crud master item) master RepHtml
|
|
postCrudAddR = crudHelper
|
|
"Add new"
|
|
(Nothing :: Maybe (Key item, item))
|
|
True
|
|
|
|
getCrudEditR :: (Yesod master, Item item, SinglePiece (Key item),
|
|
ToForm item master)
|
|
=> Text -> GHandler (Crud master item) master RepHtml
|
|
getCrudEditR s = do
|
|
itemId <- maybe notFound return $ fromSinglePiece s
|
|
crud <- getYesodSub
|
|
item <- crudGet crud itemId >>= maybe notFound return
|
|
crudHelper
|
|
"Edit item"
|
|
(Just (itemId, item))
|
|
False
|
|
|
|
postCrudEditR :: (Yesod master, Item item, SinglePiece (Key item),
|
|
ToForm item master)
|
|
=> Text -> GHandler (Crud master item) master RepHtml
|
|
postCrudEditR s = do
|
|
itemId <- maybe notFound return $ fromSinglePiece s
|
|
crud <- getYesodSub
|
|
item <- crudGet crud itemId >>= maybe notFound return
|
|
crudHelper
|
|
"Edit item"
|
|
(Just (itemId, item))
|
|
True
|
|
|
|
getCrudDeleteR :: (Yesod master, Item item, SinglePiece (Key item))
|
|
=> Text -> GHandler (Crud master item) master RepHtml
|
|
getCrudDeleteR s = do
|
|
itemId <- maybe notFound return $ fromSinglePiece s
|
|
crud <- getYesodSub
|
|
item <- crudGet crud itemId >>= maybe notFound return -- Just ensure it exists
|
|
toMaster <- getRouteToMaster
|
|
defaultLayout $ do
|
|
setTitle "Confirm delete"
|
|
addWidget [hamlet|
|
|
<form method="post" action="@{toMaster (CrudDeleteR s)}">
|
|
<h1>Really delete?
|
|
<p>Do you really want to delete #{itemTitle item}?
|
|
<p>
|
|
<input type="submit" value="Yes">
|
|
\
|
|
<a href="@{toMaster CrudListR}">No
|
|
|]
|
|
|
|
postCrudDeleteR :: (Yesod master, Item item, SinglePiece (Key item))
|
|
=> Text -> GHandler (Crud master item) master RepHtml
|
|
postCrudDeleteR s = do
|
|
itemId <- maybe notFound return $ fromSinglePiece s
|
|
crud <- getYesodSub
|
|
toMaster <- getRouteToMaster
|
|
crudDelete crud itemId
|
|
redirect RedirectTemporary $ toMaster CrudListR
|
|
|
|
crudHelper
|
|
:: (Item a, Yesod master, SinglePiece (Key a), ToForm a master)
|
|
=> Text -> Maybe (Key a, a) -> Bool
|
|
-> GHandler (Crud master a) master RepHtml
|
|
crudHelper title me isPost = do
|
|
crud <- getYesodSub
|
|
(errs, form, enctype, hidden) <- runFormPost $ toForm $ fmap snd me
|
|
toMaster <- getRouteToMaster
|
|
case (isPost, errs) of
|
|
(True, FormSuccess a) -> do
|
|
eid <- case me of
|
|
Just (eid, _) -> do
|
|
crudReplace crud eid a
|
|
return eid
|
|
Nothing -> crudInsert crud a
|
|
redirect RedirectTemporary $ toMaster $ CrudEditR
|
|
$ toSinglePiece eid
|
|
_ -> return ()
|
|
defaultLayout $ do
|
|
setTitle $ toHtml title
|
|
addWidget [hamlet|
|
|
<p>
|
|
<a href="@{toMaster CrudListR}">Return to list
|
|
<h1>#{title}
|
|
<form method="post" enctype="#{enctype}">
|
|
<table>
|
|
\^{form}
|
|
<tr>
|
|
<td colspan="2">
|
|
\#{hidden}
|
|
<input type="submit">
|
|
$maybe e <- me
|
|
\
|
|
<a href="@{toMaster (CrudDeleteR (toSinglePiece (fst e)))}">Delete
|
|
|]
|
|
|
|
-- | A default 'Crud' value which relies about persistent and "Yesod.Form".
|
|
defaultCrud
|
|
:: (PersistEntity i, PersistBackend (YesodDB a (GGHandler (Crud a i) a IO)),
|
|
YesodPersist a)
|
|
=> a -> Crud a i
|
|
defaultCrud = const Crud
|
|
{ crudSelect = runDB $ selectList [] [] 0 0
|
|
, crudReplace = \a -> runDB . replace a
|
|
, crudInsert = runDB . insert
|
|
, crudGet = runDB . get
|
|
, crudDelete = runDB . delete
|
|
}
|