By | Chris Done;Chris Done |
At | 2013-02-28; 2013-02-28 |
Title | Changed browsing to be by query string. |
Description |
Edit file amelie.cabal 33188 → 33188
47 47 ,text
48 48 ,time
49 49 ,transformers
50 50 ,utf8-string
51 51 ,mime-mail
+ 52 ,cgi
… … … … Edit file src/Main.hs 33188 → 33188
59 59 ,("/report/:id",run Report.handle)
60 60 ,("/reported",run Reported.handle)
61 61 ,("/new",run New.handle)
62 62 ,("/edit/:id",run New.handle)
63 63 ,("/new/:channel",run New.handle)
- 64 ,("/browse/page/:page/offset/:offset",run Browse.handle)
- 65 ,("/browse/page/:page",run Browse.handle)
66 64 ,("/browse",run Browse.handle)
67 65 ,("/activity",run Activity.handle)
68 66 ,("/diff/:this/:that",run Diff.handle)
69 67 ]
70 68 run = runHandler conf p cache ans
… … … … Edit file src/Text/Blaze/Extra.hs 33188 → 33188
+ 1 {-# OPTIONS -fno-warn-orphans #-}
1 2 {-# LANGUAGE RecordWildCards #-}
2 3 {-# LANGUAGE OverloadedStrings #-}
3 4 {-# OPTIONS -fno-warn-name-shadowing -fno-warn-unused-do-bind #-}
4 5 5 6 module Text.Blaze.Extra where
… … … … 8 9 import Data.Monoid
9 10 import Data.Monoid.Operator
10 11 import Prelude hiding ((++),head,div)
11 12 import Text.Blaze.Html5 as H hiding (map)
12 13 import Text.Blaze.Html5.Attributes as A
+ 14 import Text.Blaze.Internal (Attributable)
+ 15 import Network.URI.Params
13 16 import Network.URI
14 17 import Text.Printf
15 18 import Data.List (intercalate)
16 19 + 20 (!.) :: (Attributable h) => h -> AttributeValue -> h
17 21 elem !. className = elem ! class_ className
18 22 + 23 (!#) :: (Attributable h) => h -> AttributeValue -> h
19 24 elem !# idName = elem ! A.id idName
20 25 21 26 linesToHtml :: String -> Html
22 27 linesToHtml str = forM_ (lines str) $ \line -> do toHtml line; br
23 28 … … … … 30 35 htmlCommasAnd [x] = x
31 36 htmlCommasAnd [x,y] = do x; " and "; y
32 37 htmlCommasAnd (x:xs) = do x; ", "; htmlCommasAnd xs
33 38 htmlCommasAnd [] = mempty
34 39 + 40 htmlCommas :: [Html] -> Html
35 41 htmlCommas = htmlIntercalate ", "
+ 42 + 43 hrefSet :: URI -> String -> String -> Attribute
+ 44 hrefSet uri key value = hrefURI updated where
+ 45 updated = updateUrlParam key value uri
+ 46 + 47 hrefURI :: URI -> Attribute
+ 48 hrefURI uri = href (toValue (showURI uri)) where
+ 49 showURI URI{..} = uriPath ++ uriQuery
+ 50 + 51 hrefAssoc :: String -> [(String,String)] -> Attribute
+ 52 hrefAssoc path qs = href (toValue uri) where
+ 53 uri = "/" ++ path ++ "?" ++ intercalate "&" (map (uncurry (printf "%s=%s")) qs)
+ 54 + 55 instance ToValue URI where
+ 56 toValue = toValue . show
… … … … Edit file src/Amelie/Controller.hs 33188 → 33188
10 10 ,outputText
11 11 ,goHome
12 12 ,justOrGoHome
13 13 ,getInteger
14 14 ,getString
+ 15 ,getStringMaybe
15 16 ,getPagination)
16 17 where
17 18 18 19 import Amelie.Types
19 20 import Amelie.Types.Cache
20 21 21 22 import Control.Applicative
22 23 import Control.Concurrent.Chan (Chan)
+ 24 import Control.Monad.Env
23 25 import Control.Monad.Reader (runReaderT)
24 26 import Data.ByteString (ByteString)
25 27 import Data.ByteString.UTF8 (toString)
+ 28 import Data.Maybe
+ 29 import Network.URI
26 30 import Data.Text.Lazy (Text,toStrict)
27 31 import Database.PostgreSQL.Base (withPoolConnection)
28 32 import Database.PostgreSQL.Simple (Pool)
29 33 import Safe (readMay)
- 30 import Snap.Core (Snap,writeText,redirect,getParam)
- 31 import Snap.Core (modifyResponse,setContentType)
+ 34 import Snap.Core
32 35 import Text.Blaze (Html)
33 36 import Text.Blaze.Renderer.Text (renderHtml)
34 37 35 38 -- | Run a controller handler.
36 39 runHandler :: Config -> Pool -> Cache -> Chan Text -> Controller () -> Snap ()
… … … … 73 76 getString :: ByteString -> String -> Controller String
74 77 getString name def = do
75 78 pid <- (>>= return . toString) <$> getParam name
76 79 maybe (return def) return pid
77 80 + 81 -- | Get string (maybe).
+ 82 getStringMaybe :: ByteString -> Controller (Maybe String)
+ 83 getStringMaybe name = do
+ 84 pid <- (>>= return . toString) <$> getParam name
+ 85 return pid
+ 86 78 87 -- | Get pagination data.
79 88 getPagination :: Controller Pagination
80 89 getPagination = do
81 90 p <- getInteger "page" 1
82 91 limit <- getInteger "limit" 35
+ 92 i <- fmap rqURI getRequest
+ 93 uri <- getMyURI
83 94 return Pagination { pnPage = max 1 p
84 95 , pnLimit = max 1 (min 100 limit)
- 85 , pnRoot = "/"
+ 96 , pnURI = uri
86 97 , pnResults = 0
87 98 , pnTotal = 0
88 99 }
+ 100 + 101 getMyURI = do
+ 102 domain <- env (configDomain . controllerStateConfig)
+ 103 fmap (fromJust .
+ 104 parseURI .
+ 105 (("http://" ++ domain) ++) .
+ 106 toString .
+ 107 rqURI)
+ 108 getRequest
… … … … Edit file src/Amelie/View/Html.hs 33188 → 33188
27 27 import Data.Maybe (fromMaybe)
28 28 import Data.Monoid.Operator ((++))
29 29 import Data.Text (pack)
30 30 import Data.Text.Lazy (Text)
31 31 import qualified Data.Text.Lazy as T
+ 32 import Network.URI.Params
32 33 import Prelude hiding ((++))
33 34 import Text.Blaze.Html5 as H hiding (map,nav)
34 35 import qualified Text.Blaze.Html5.Attributes as A
+ 36 import Text.Blaze.Extra
35 37 36 38 -- | A class prefixed with amelie-.
37 39 aClass :: AttributeValue -> Attribute
38 40 aClass name = A.class_ ("amelie-" ++ name)
39 41 … … … … 128 130 end = pnPage * pnResults
129 131 130 132 -- | Link to change navigation page based on a direction.
131 133 navDirection :: Pagination -> Integer -> Text -> Html
132 134 navDirection Pagination{..} change caption = do
- 133 href url caption
- 134 - 135 where url = pnRoot ++ "/page/" ++
- 136 pack (show $ pnPage + change)
+ 135 a ! hrefURI uri $ toHtml caption
+ 136 + 137 where uri = updateUrlParam "page"
+ 138 (show (pnPage + change))
+ 139 pnURI
+ 140 … … … … Edit file src/Amelie/Types/View.hs 33188 → 33188
1 1 module Amelie.Types.View
2 2 (Pagination(..))
3 3 where
4 4 - 5 import Data.Text (Text)
+ 5 import Data.Map (Map)
+ 6 import Data.ByteString (ByteString)
+ 7 import Network.URI (URI)
6 8 7 9 -- | Pagination data.
8 10 data Pagination = Pagination {
9 11 pnPage :: Integer
10 12 , pnLimit :: Integer
- 11 , pnRoot :: Text
+ 13 , pnURI :: URI
12 14 , pnResults :: Integer
13 15 , pnTotal :: Integer
14 16 } deriving Show
… … … … Edit file src/Amelie/Model/Paste.hs 33188 → 33188
39 39 import Prelude hiding ((++))
40 40 import System.Directory
41 41 import System.FilePath
42 42 43 43 -- | Count public pastes.
- 44 countPublicPastes :: Model Integer
- 45 countPublicPastes = do
- 46 rows <- singleNoParams ["SELECT COUNT(*)"
- 47 ,"FROM public_toplevel_paste"]
+ 44 countPublicPastes :: Maybe String -> Model Integer
+ 45 countPublicPastes mauthor = do
+ 46 rows <- single ["SELECT COUNT(*)"
+ 47 ,"FROM public_toplevel_paste"
+ 48 ,"WHERE (? IS NULL) OR (author = ?)"]
+ 49 (mauthor,mauthor)
48 50 return $ fromMaybe 0 rows
49 51 50 52 -- | Get the latest pastes.
51 53 getLatestPastes :: Model [Paste]
52 54 getLatestPastes =
… … … … 54 56 ,"FROM public_toplevel_paste"
55 57 ,"ORDER BY id DESC"
56 58 ,"LIMIT 20"]
57 59 58 60 -- | Get some paginated pastes.
- 59 getSomePastes :: Pagination -> Model [Paste]
- 60 getSomePastes Pagination{..} =
- 61 queryNoParams ["SELECT *"
- 62 ,"FROM public_toplevel_paste"
- 63 ,"ORDER BY id DESC"
- 64 ,"OFFSET " ++ show (max 0 (pnPage - 1) * pnLimit)
- 65 ,"LIMIT " ++ show pnLimit]
+ 61 getSomePastes :: Maybe String -> Pagination -> Model [Paste]
+ 62 getSomePastes mauthor Pagination{..} =
+ 63 query ["SELECT *"
+ 64 ,"FROM public_toplevel_paste"
+ 65 ,"WHERE (? IS NULL) OR (author = ?)"
+ 66 ,"ORDER BY id DESC"
+ 67 ,"OFFSET " ++ show (max 0 (pnPage - 1) * pnLimit)
+ 68 ,"LIMIT " ++ show pnLimit]
+ 69 (mauthor,mauthor)
66 70 67 71 -- | Get a paste by its id.
68 72 getPasteById :: PasteId -> Model (Maybe Paste)
69 73 getPasteById pid =
70 74 listToMaybe <$> query ["SELECT *"
… … … … Edit file src/Amelie/Controller/Browse.hs 33188 → 33188
5 5 6 6 module Amelie.Controller.Browse
7 7 (handle)
8 8 where
9 9 - 10 import Amelie.Controller (output,getPagination)
+ 10 import Amelie.Controller (output,getPagination,getStringMaybe)
11 11 import Amelie.Model
12 12 import Amelie.Model.Channel (getChannels)
13 13 import Amelie.Model.Language (getLanguages)
14 14 import Amelie.Model.Paste (getSomePastes,countPublicPastes)
15 15 import Amelie.View.Browse (page)
16 16 17 17 -- | Browse all pastes.
18 18 handle :: Controller ()
19 19 handle = do
20 20 pn <- getPagination
- 21 total <- model countPublicPastes
- 22 pastes <- model $ getSomePastes pn
- 23 let pn' = pn { pnRoot = "/browse"
- 24 , pnResults = fromIntegral (length pastes)
+ 21 author <- getStringMaybe "author"
+ 22 total <- model $ countPublicPastes author
+ 23 pastes <- model $ getSomePastes author pn
+ 24 let pn' = pn { pnResults = fromIntegral (length pastes)
25 25 , pnTotal = total }
26 26 chans <- model getChannels
27 27 langs <- model getLanguages
28 28 output $ page pn' chans langs pastes
… … … … Edit file src/Amelie/Controller/Reported.hs 33188 → 33188
16 16 handle :: Controller ()
17 17 handle = do
18 18 pn <- getPagination
19 19 total <- model countReports
20 20 reports <- model $ getSomeReports pn
- 21 let pn' = pn { pnRoot = "/reported"
- 22 , pnResults = fromIntegral (length reports)
+ 21 let pn' = pn { pnResults = fromIntegral (length reports)
23 22 , pnTotal = total }
24 23 output $ page pn' reports
… … … … Add file src/Network/URI/Params.hs 33188
+ 1 {-# LANGUAGE NamedFieldPuns #-} + 2 {-# OPTIONS -fno-warn-missing-signatures #-} + 3 module Network.URI.Params (updateUrlParam,updateUrlParams,uriParams,deleteQueryKey) where + 4 + 5 import Control.Arrow + 6 import Network.URI + 7 import Data.List + 8 import Data.Function + 9 import Network.CGI + 10 + 11 updateUrlParam :: String -> String -> URI -> URI + 12 updateUrlParam this value uri@(URI{uriQuery}) = + 13 uri { uriQuery = updated uriQuery } where + 14 updated = editQuery $ ((this,value):) . deleteBy ((==) `on` fst) (this,"") + 15 + 16 deleteQueryKey :: String -> URI -> URI + 17 deleteQueryKey key uri = + 18 uri { uriQuery = editQuery (filter ((/=key).fst)) (uriQuery uri) } + 19 + 20 editQuery :: ([(String,String)] -> [(String,String)]) -> String -> String + 21 editQuery f = ('?':) . formEncodeUrl . f . formDecode . dropWhile (=='?') + 22 + 23 formEncodeUrl = intercalate "&" . map keyval . map (esc *** esc) + 24 where keyval (key,val) = key ++ "=" ++ val + 25 esc = escapeURIString isAllowedInURI + 26 + 27 updateUrlParams :: [(String,String)] -> URI -> URI + 28 updateUrlParams = flip $ foldr $ uncurry updateUrlParam + 29 + 30 uriParams :: URI -> [(String,String)] + 31 uriParams = formDecode . dropWhile (=='?') . uriQuery