Added blog sample

This commit is contained in:
Michael Snoyman 2010-07-02 09:19:26 +03:00
parent f2e71a6c00
commit 65d8e2febd
4 changed files with 137 additions and 13 deletions

1
.gitignore vendored
View File

@ -3,3 +3,4 @@
client_session_key.aes client_session_key.aes
*.hi *.hi
*.o *.o
blog.db3

View File

@ -29,6 +29,9 @@ module Yesod.Form
, htmlField , htmlField
, stringInput , stringInput
, fieldsToTable , fieldsToTable
, share2
, mkIsForm
, mapFormXml
{- FIXME {- FIXME
-- * Create your own formlets -- * Create your own formlets
, incr , incr
@ -102,6 +105,11 @@ newtype GForm sub y xml a = GForm
type Form sub y = GForm sub y (Widget sub y ()) type Form sub y = GForm sub y (Widget sub y ())
type FormField sub y = GForm sub y [FieldInfo sub y] type FormField sub y = GForm sub y [FieldInfo sub y]
mapFormXml :: (xml1 -> xml2) -> GForm s y xml1 a -> GForm s y xml2 a
mapFormXml f (GForm g) = GForm $ \e fe -> do
(res, xml, enc) <- g e fe
return (res, f xml, enc)
data FieldInfo sub y = FieldInfo data FieldInfo sub y = FieldInfo
{ fiLabel :: Html () { fiLabel :: Html ()
, fiTooltip :: Html () , fiTooltip :: Html ()
@ -478,6 +486,7 @@ instance Formable Slug where
| all (\c -> c `elem` "-_" || isAlphaNum c) x' = | all (\c -> c `elem` "-_" || isAlphaNum c) x' =
Right $ Slug x' Right $ Slug x'
| otherwise = Left ["Slug must be alphanumeric, - and _"] | otherwise = Left ["Slug must be alphanumeric, - and _"]
-}
share2 :: Monad m => (a -> m [b]) -> (a -> m [b]) -> a -> m [b] share2 :: Monad m => (a -> m [b]) -> (a -> m [b]) -> a -> m [b]
share2 f g a = do share2 f g a = do
@ -485,8 +494,8 @@ share2 f g a = do
g' <- g a g' <- g a
return $ f' ++ g' return $ f' ++ g'
deriveFormable :: [EntityDef] -> Q [Dec] mkIsForm :: [EntityDef] -> Q [Dec]
deriveFormable = mapM derive mkIsForm = mapM derive
where where
derive :: EntityDef -> Q Dec derive :: EntityDef -> Q Dec
derive t = do derive t = do
@ -496,24 +505,31 @@ deriveFormable = mapM derive
just <- [|pure|] just <- [|pure|]
nothing <- [|Nothing|] nothing <- [|Nothing|]
let just' = just `AppE` ConE (mkName $ entityName t) let just' = just `AppE` ConE (mkName $ entityName t)
string' <- [|string|]
mempty' <- [|mempty|]
mfx <- [|mapFormXml|]
ftt <- [|fieldsToTable|]
let go_ = go ap just' string' mempty' mfx ftt
let c1 = Clause [ ConP (mkName "Nothing") [] let c1 = Clause [ ConP (mkName "Nothing") []
] ]
(NormalB $ go ap just' $ zip cols $ map (const nothing) cols) (NormalB $ go_ $ zip cols $ map (const nothing) cols)
[] []
xs <- mapM (const $ newName "x") cols xs <- mapM (const $ newName "x") cols
let xs' = map (AppE just . VarE) xs let xs' = map (AppE just . VarE) xs
let c2 = Clause [ ConP (mkName "Just") [ConP (mkName $ entityName t) let c2 = Clause [ ConP (mkName "Just") [ConP (mkName $ entityName t)
$ map VarP xs]] $ map VarP xs]]
(NormalB $ go ap just' $ zip cols xs') (NormalB $ go_ $ zip cols xs')
[] []
return $ InstanceD [] (ConT ''Formable return $ InstanceD [] (ConT ''IsForm
`AppT` ConT (mkName $ entityName t)) `AppT` ConT (mkName $ entityName t))
[FunD (mkName "formable") [c1, c2]] [FunD (mkName "toForm") [c1, c2]]
go ap just' = foldl (ap' ap) just' . map go' go ap just' string' mem mfx ftt a =
go' (label, ex) = let x = foldl (ap' ap) just' $ map (go' string' mem) a
VarE (mkName "sealForm") `AppE` in mfx `AppE` ftt `AppE` x
(VarE (mkName "wrapperRow") `AppE` LitE (StringL label)) `AppE` go' string' mempty' (label, ex) =
(VarE (mkName "formable") `AppE` ex) let label' = string' `AppE` LitE (StringL label)
in VarE (mkName "toFormField") `AppE` label'
`AppE` mempty' `AppE` ex
ap' ap x y = InfixE (Just x) ap (Just y) ap' ap x y = InfixE (Just x) ap (Just y)
toLabel :: String -> String toLabel :: String -> String
@ -524,4 +540,3 @@ toLabel (x:rest) = toUpper x : go rest
go (c:cs) go (c:cs)
| isUpper c = ' ' : c : go cs | isUpper c = ' ' : c : go cs
| otherwise = c : go cs | otherwise = c : go cs
-}

View File

@ -103,7 +103,7 @@ class Yesod a where
-- Return 'Nothing' is the request is authorized, 'Just' a message if -- Return 'Nothing' is the request is authorized, 'Just' a message if
-- unauthorized. If authentication is required, you should use a redirect; -- unauthorized. If authentication is required, you should use a redirect;
-- the Auth helper provides this functionality automatically. -- the Auth helper provides this functionality automatically.
isAuthorized :: Routes a -> GHandler s a (Maybe String) isAuthorized :: Routes a -> GHandler s a (Maybe String) -- FIXME use a data type that specifies whether authentication is required
isAuthorized _ = return Nothing isAuthorized _ = return Nothing
-- | A type-safe, concise method of creating breadcrumbs for pages. For each -- | A type-safe, concise method of creating breadcrumbs for pages. For each

108
blog.hs Normal file
View File

@ -0,0 +1,108 @@
{-# LANGUAGE TypeFamilies, QuasiQuotes, GeneralizedNewtypeDeriving #-}
import Yesod
import Yesod.Helpers.Auth
import Yesod.Helpers.Crud
import Database.Persist.Sqlite
import Data.Time (Day)
type Html' = Html ()
share2 mkPersist mkIsForm [$persist|
Entry
title String
posted Day Desc
content Html'
deriving
|]
instance Item Entry where
itemTitle = entryTitle
getAuth = const $ Auth
{ authIsOpenIdEnabled = False
, authRpxnowApiKey = Nothing
, authEmailSettings = Nothing
-- | client id, secret and requested permissions
, authFacebook = Just (clientId, secret, ["email"])
}
where
clientId = "134280699924829"
secret = "a7685e10c8977f5435e599aaf1d232eb"
data Blog = Blog Connection
type EntryCrud = Crud Blog Entry
mkYesod "Blog" [$parseRoutes|
/ RootR GET
/entry/#EntryId EntryR GET
/admin AdminR EntryCrud defaultCrud
/auth AuthR Auth getAuth
|]
instance Yesod Blog where
approot _ = "http://localhost:3000"
defaultLayout p = do
mcreds <- maybeCreds
admin <- maybeAuthorized $ AdminR CrudListR
hamletToContent [$hamlet|
!!!
%html
%head
%title $pageTitle.p$
^pageHead.p^
%body
%p
%a!href=@RootR@ Homepage
$maybe admin a
\ | $
%a!href=@a@ Admin
\ | $
$maybe mcreds c
Welcome $
$maybe credsDisplayName.c dn
$string.dn$
$nothing
$string.credsIdent.c$
\ $
%a!href=@AuthR.Logout@ Logout
$nothing
%a!href=@AuthR.StartFacebookR@ Facebook Connect
^pageBody.p^
%p
Powered by Yesod Web Framework
|]
isAuthorized AdminR{} = do
mc <- maybeCreds
let x = (mc >>= credsEmail) == Just "michael@snoyman.com"
return $ if x then Nothing else Just "Permission denied"
isAuthorized _ = return Nothing
instance YesodAuth Blog where
defaultDest _ = RootR
defaultLoginRoute _ = RootR
instance YesodPersist Blog where
type YesodDB Blog = SqliteReader
runDB db = do
Blog conn <- getYesod
runSqlite db conn
getRootR = do
entries <- runDB $ select [] [EntryPostedDesc]
applyLayoutW $ do
setTitle $ string "Blog tutorial homepage"
addBody [$hamlet|
%h1 All Entries
%ul
$forall entries entry
%li
%a!href=@EntryR.fst.entry@ $string.entryTitle.snd.entry$
|]
getEntryR :: EntryId -> Handler Blog RepHtml
getEntryR eid = do
entry <- runDB (get eid) >>= maybe notFound return
applyLayoutW $ do
setTitle $ string $ entryTitle entry
addBody [$hamlet|
%h1 $string.entryTitle.entry$
%h2 $string.show.entryPosted.entry$
#content $entryContent.entry$
|]
main = withSqlite "blog.db3" $ \conn -> do
flip runSqlite conn $ initialize (undefined :: Entry)
toWaiApp (Blog conn) >>= basicHandler 3000