Advertisement
Guest User

Untitled

a guest
Dec 21st, 2020
191
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. {-# LANGUAGE DeriveDataTypeable #-}
  2. {-# LANGUAGE DerivingStrategies #-}
  3. {-# LANGUAGE FlexibleContexts #-}
  4. {-# LANGUAGE GADTs #-}
  5. {-# LANGUAGE GeneralizedNewtypeDeriving #-}
  6. {-# LANGUAGE MultiParamTypeClasses #-}
  7. {-# LANGUAGE OverloadedStrings #-}
  8. {-# LANGUAGE QuasiQuotes #-}
  9. {-# LANGUAGE StandaloneDeriving #-}
  10. {-# LANGUAGE TemplateHaskell #-}
  11. {-# LANGUAGE TypeFamilies #-}
  12. {-# LANGUAGE UndecidableInstances #-}
  13. {-# LANGUAGE ViewPatterns #-}
  14.  
  15. import Control.Applicative
  16.   ( pure,
  17.     (<$>),
  18.     (<*>),
  19.   )
  20. import Control.Monad.Logger (runStdoutLoggingT)
  21. import Data.Text (Text)
  22. import Data.Time
  23.   ( UTCTime,
  24.     getCurrentTime,
  25.   )
  26. import Data.Typeable (Typeable)
  27. import Database.Persist.Sqlite
  28.   ( ConnectionPool,
  29.     SqlBackend,
  30.     createSqlitePool,
  31.     runMigration,
  32.     runSqlPersistMPool,
  33.     runSqlPool,
  34.   )
  35. import Network.HTTP.Client.TLS (tlsManagerSettings)
  36. import Network.HTTP.Conduit
  37.   ( Manager,
  38.     newManager,
  39.   )
  40. import Yesod
  41. import Yesod.Auth
  42. import Yesod.Auth.OpenId
  43.   ( IdentifierType (..),
  44.     authOpenId,
  45.   )
  46. import Yesod.Form.Nic
  47.   ( YesodNic,
  48.     nicHtmlField,
  49.   )
  50.  
  51. share
  52.   [mkPersist sqlSettings, mkMigrate "migrateAll"]
  53.   [persistLowerCase|
  54. User
  55.     email Text
  56.     UniqueUser email
  57.     deriving Typeable
  58. Entry
  59.     title Text
  60.     posted UTCTime
  61.     content Html
  62. Comment
  63.     entry EntryId
  64.     posted UTCTime
  65.     user UserId
  66.     name Text
  67.     text Textarea
  68. |]
  69.  
  70. data Blog = Blog
  71.   { connPool :: ConnectionPool,
  72.     httpManager :: Manager
  73.   }
  74.  
  75. mkMessage "Blog" "blog-messages" "en"
  76.  
  77. mkYesod
  78.   "Blog"
  79.   [parseRoutes|
  80. /              HomeR  GET
  81. /blog          BlogR  GET  POST
  82. /blog/#EntryId EntryR GET  POST
  83. /auth          AuthR  Auth getAuth
  84. |]
  85.  
  86. instance Yesod Blog where
  87.   approot = ApprootStatic "http://localhost:3000"
  88.  
  89.   isAuthorized BlogR True = do
  90.     mauth <- maybeAuth
  91.     case mauth of
  92.       Nothing -> return AuthenticationRequired
  93.       Just (Entity _ user)
  94.         | isAdmin user -> return Authorized
  95.         | otherwise -> unauthorizedI MsgNotAnAdmin
  96.   isAuthorized (EntryR _) True = do
  97.     mauth <- maybeAuth
  98.     case mauth of
  99.       Nothing -> return AuthenticationRequired
  100.       Just _ -> return Authorized
  101.   isAuthorized _ _ = return Authorized
  102.  
  103.   authRoute _ = Just (AuthR LoginR)
  104.  
  105.   defaultLayout inside = do
  106.     mmsg <- getMessage
  107.     pc <- widgetToPageContent $ do
  108.       toWidget
  109.         [lucius|
  110. body {
  111.   width: 760px;
  112.   margin: 1em auto;
  113.   font-family: sans-serif;
  114. }
  115. textarea {
  116.   width: 400px;
  117.   height: 200px;
  118. }
  119. #message {
  120.   color: #900
  121. }
  122. |]
  123.       inside
  124.     withUrlRenderer
  125.       [hamlet|
  126. $doctype 5
  127. <html>
  128.   <head>
  129.     <title>#{pageTitle pc}
  130.     ^{pageHead pc}
  131.   <body>
  132.     $maybe msg <- mmsg
  133.       <div #message>#{msg}
  134.     ^{pageBody pc}
  135. |]
  136.  
  137. isAdmin :: User -> Bool
  138. isAdmin user = userEmail user == "foo@bar"
  139.  
  140. instance YesodPersist Blog where
  141.   type YesodPersistBackend Blog = SqlBackend
  142.   runDB f = do
  143.     master <- getYesod
  144.     let pool = connPool master
  145.     runSqlPool f pool
  146.  
  147. -- type Form x = Html -> Form Handler (FormResult x, Widget)
  148.  
  149. instance RenderMessage Blog FormMessage where
  150.   renderMessage _ _ = defaultFormMessage
  151.  
  152. instance YesodNic Blog
  153.  
  154. instance YesodAuth Blog where
  155.   type AuthId Blog = UserId
  156.   loginDest _ = HomeR
  157.   logoutDest _ = HomeR
  158.   authPlugins _ =
  159.     [ authOpenId
  160.         Claimed
  161.         [ ("openid.ns.ax", "http://openid.net/srv/ax/1.0"),
  162.           ("open.ax.mode", "fetch_request"),
  163.           ("open.ax.type.email", "http://axschema.org/contact/email"),
  164.           ("openid.ax.required", "email")
  165.         ]
  166.     ]
  167.   getAuthId creds =
  168.     let emailKey = "openid.ax.value.email"
  169.      in case lookup emailKey (credsExtra creds) of
  170.           Just email -> do
  171.             res <- liftHandler $ runDB $ insertBy (User email)
  172.             return $ Just $ either entityKey id res
  173.           Nothing -> return Nothing
  174.  
  175. instance YesodAuthPersist Blog
  176.  
  177. getHomeR :: Handler Html
  178. getHomeR = defaultLayout $ do
  179.   setTitleI MsgHomepageTitle
  180.   [whamlet|
  181. <p>_{MsgWelcomeHomepage}
  182. <p>
  183.   <a href=@{BlogR}>_{MsgSeeArchive}
  184. |]
  185.  
  186. entryForm :: Form Entry
  187. entryForm =
  188.   renderDivs $
  189.     Entry
  190.       <$> areq textField (fieldSettingsLabel MsgNewEntryTitle) Nothing
  191.       <*> lift (liftIO getCurrentTime)
  192.       <*> areq nicHtmlField (fieldSettingsLabel MsgNewEntryContent) Nothing
  193.  
  194. getBlogR :: Handler Html
  195. getBlogR = do
  196.   muser <- maybeAuth
  197.   entries <- runDB $ selectList [] [Desc EntryPosted]
  198.   (entryWidget, enctype) <- generateFormPost entryForm
  199.   defaultLayout $ do
  200.     setTitleI MsgBlogArchiveTitle
  201.     [whamlet|
  202. $if null entries
  203.   <p>_{MsgNoEntries}
  204. $else
  205.   <ul>
  206.     $forall Entity entryId entry <- entries
  207.       <li>
  208.         <a href=@{EntryR entryId}>#{entryTitle entry}
  209. $maybe Entity _ user <- muser
  210.   $if isAdmin user
  211.     <form method=post enctype=#{enctype}>
  212.       ^{entryWidget}
  213.       <div>
  214.         <input type=submit value=_{MsgNewEntry}>
  215. $nothing
  216.   <p>
  217.     <a href=@{AuthR LoginR}>_{MsgLoginToPost}
  218. |]
  219.  
  220. postBlogR :: Handler Html
  221. postBlogR = do
  222.   ((res, entryWidget), enctype) <- runFormPost entryForm
  223.   case res of
  224.     FormSuccess entry -> do
  225.       entryId <- runDB $ insert entryForm
  226.       setMessageI $ MsgEntryCreated $ entryTitle entry
  227.       redirect $ EntryR entryId
  228.     _ -> defaultLayout $ do
  229.       setTitleI MsgPleaseCorrectEntry
  230.       [whamlet|
  231. <form method=post enctype=#{enctype}>
  232.   ^{entryWidget}
  233.   <div>
  234.     <input type=submit value=_{MsgNewEntry}>      
  235. |]
  236.  
  237. commentForm :: EntryId -> Form Comment
  238. commentForm entryId =
  239.   renderDivs $
  240.     Comment
  241.       <$> pure EntryId
  242.       <*> lift (liftIO getCurrentTime)
  243.       <*> lift requireAuthId
  244.       <*> areq textField (fieldSettingsLabel MsgCommentName) Nothing
  245.       <*> areq textareaField (fieldSettingsLabel MsgCommentText) Nothing
  246.  
  247. getEntryR :: EntryId -> Handler Html
  248. getEntryR entryId = do
  249.   (entry, comments) <- runDB $ do
  250.     entry <- get404 entryId
  251.     comments <- selectList [CommentEntry ==. entryId] [Asc CommentPosted]
  252.     return (entry, map entityVal comments)
  253.   muser <- maybeAuth
  254.   (commentWidget, enctype) <- generateFormPost (commentForm entryId)
  255.   defaultLayout $ do
  256.     setTitleI $ MsgEntryTitle $ entryTitle entry
  257.     [whamlet|
  258. <h1>#{entryTitle entry}
  259. <article>#{entryContent entry}
  260.   <section .comments>
  261.     <h1>_{MsgCommentsHeading}
  262.     $if null comments
  263.       <p>_{MsgNoComments}
  264.     $else
  265.       $forall Comment _entry posted _user name text <- comments
  266.         <div .comment>
  267.           <span .by>#{name}
  268.           <span .at>#{show posted}
  269.           <div .content>#{text}
  270.     <section>
  271.       <h1>_{MsgAddCommentHeading}
  272.       $maybe _ <- muser
  273.         <form method=post enctype=#{enctype}>
  274.           ^{commentWidget}
  275.           <div>
  276.             <input type=submit value=_{MsgAddCommentButton}>
  277.       $nothing
  278.         <p>
  279.           <a href=@{AuthR LoginR}>_{MsgLoginToComment}    
  280. |]
  281.  
  282. postEntryR :: EntryId -> Handler Html
  283. postEntryR entryId = do
  284.   ((res, commentWidget), enctype) <- runFormPost (commentForm entryId)
  285.   case res of
  286.     FormSuccess comment -> do
  287.       _ <- runDB $ insert commentForm
  288.       setMessageI MsgCommentAdded
  289.       redirect $ EntryR entryId
  290.     _ -> defaultLayout $ do
  291.       setTitleI MsgPleaseCorrectComment
  292.       [whamlet|
  293. <form method=post enctype=#{enctype}>
  294.   ^{commentWidget}
  295.   <div>
  296.     <input type=submit value=_{MsgAddCommentButton}>      
  297. |]
  298.  
  299. main :: IO ()
  300. main = do
  301.   pool <- runStdoutLoggingT $ createSqlitePool "blog.db3" 10
  302.   runSqlPersistMPool (runMigration migrateAll) pool
  303.   manager <- newManager tlsManagerSettings
  304.   warp 3000 $ Blog pool manager
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement