module Yesod.Crud where import Prelude import Control.Applicative import Data.Maybe import Lens.Micro.TH import Yesod.Core import Database.Persist (Key) import Network.Wai (pathInfo, requestMethod) import Yesod.Persist hiding (Key) import Database.Persist.Sql import Data.Foldable (for_) import Data.Text (Text) import Data.Monoid import qualified Data.List as List import qualified Database.Esqueleto as E import Yesod.OldCrud.Internal import Data.Typeable import Data.Proxy import Unsafe.Coerce (unsafeCoerce) -- In Crud, c is the child type, and p is the type of the identifier -- for its parent. data CrudHandler site p c = CrudHandler { _chAdd :: p -> HandlerT site IO Html , _chIndex :: p -> HandlerT site IO Html , _chEdit :: Key c -> HandlerT site IO Html , _chDelete :: Key c -> HandlerT site IO Html , _chView :: Key c -> HandlerT site IO Html } makeLenses ''CrudHandler data CrudRoute p c = AddR p | IndexR p | EditR (Key c) | DeleteR (Key c) | ViewR (Key c) data EditParent = EditParentView | EditParentIndex handleCrud :: CrudHandler site p c -> CrudRoute p c -> HandlerT site IO Html handleCrud (CrudHandler add index edit delete view) route = case route of AddR p -> add p IndexR p -> index p EditR p -> edit p DeleteR p -> delete p ViewR p -> view p instance (PathPiece (Key c), Eq (Key c), PathPiece p, Eq p) => PathMultiPiece (CrudRoute p c) where fromPathMultiPiece xs = Nothing <|> (runSM xs $ pure EditR <* consumeMatchingText "edit" <*> consumeKey) <|> (runSM xs $ pure DeleteR <* consumeMatchingText "delete" <*> consumeKey) <|> (runSM xs $ pure IndexR <* consumeMatchingText "index" <*> consumeKey) <|> (runSM xs $ pure AddR <* consumeMatchingText "add" <*> consumeKey) <|> (runSM xs $ pure ViewR <* consumeMatchingText "view" <*> consumeKey) toPathMultiPiece r = case r of EditR theId -> ["edit", toPathPiece theId] DeleteR theId -> ["delete", toPathPiece theId] IndexR p -> ["index", toPathPiece p] AddR p -> ["add", toPathPiece p] ViewR theId -> ["view", toPathPiece theId] breadcrumbsCrud :: PersistCrudEntity site c => EditParent -> (CrudRoute p c -> Route site) -> CrudRoute p c -> Text -> (Entity c -> Text) -> (Key c -> YesodDB site p) -> (p -> YesodDB site (Maybe (Route site))) -> HandlerT site IO (Text, Maybe (Route site)) breadcrumbsCrud editParent tp route indexName getName getParent getIndexParent = case route of AddR p -> return ("Add", Just $ tp $ IndexR p) IndexR p -> do indexParent <- runDB $ getIndexParent p return (indexName, indexParent) ViewR cid -> do c <- runDB $ get404 cid p <- runDB $ getParent cid return (getName (Entity cid c), Just $ tp $ IndexR p) EditR cid -> case editParent of EditParentView -> return ("Edit", Just $ tp $ ViewR cid) EditParentIndex -> do c <- runDB $ get404 cid p <- runDB $ getParent cid return ("Edit - " <> getName (Entity cid c), Just $ tp $ IndexR p) DeleteR cid -> return ("Delete", Just $ tp $ ViewR cid) deriving instance (Eq (Key c), Eq p) => Eq (CrudRoute p c) deriving instance (Show (Key c), Show p) => Show (CrudRoute p c) deriving instance (Read (Key c), Read p) => Read (CrudRoute p c) type PersistCrudEntity site a = ( PathPiece (Key a) , Yesod site , YesodPersist site , PersistEntity a , PersistQuery (YesodPersistBackend site) , PersistEntityBackend a ~ YesodPersistBackend site )