@@ -3,8 +3,8 @@
3module Hakyll.Web.Paginate
3module Hakyll.Web.Paginate
4 ( PageNumber
4 ( PageNumber
5 , Paginate (..)
5 , Paginate (..)
7 , buildPaginateWith
6 , buildPaginateWith
8 , paginateRules
8 , paginateRules
9 , paginateContext
9 , paginateContext
@@ -12,11 +12,9 @@ module Hakyll.Web.Paginate
13--------------------------------------------------------------------------------
13--------------------------------------------------------------------------------
14import Control.Monad (forM_)
14import Control.Monad (forM_)
15−import Data.List (unfoldr)
16import qualified Data.Map as M
15import qualified Data.Map as M
17import Data.Monoid (mconcat)
16import Data.Monoid (mconcat)
18import qualified Data.Set as S
17import qualified Data.Set as S
19−import Text.Printf (printf)
22--------------------------------------------------------------------------------
20--------------------------------------------------------------------------------
@@ -37,99 +35,93 @@ type PageNumber = Int
37--------------------------------------------------------------------------------
35--------------------------------------------------------------------------------
38-- | Data about paginators
36-- | Data about paginators
39data Paginate = Paginate
37data Paginate = Paginate
40− { paginatePages :: M.Map PageNumber [Identifier]
38+ { paginateMap :: M.Map PageNumber [Identifier]
41− , paginatePlaces :: M.Map Identifier PageNumber
42 , paginateMakeId :: PageNumber -> Identifier
39 , paginateMakeId :: PageNumber -> Identifier
43 , paginateDependency :: Dependency
40 , paginateDependency :: Dependency
44 } deriving (Show)
41 } deriving (Show)
47--------------------------------------------------------------------------------
44--------------------------------------------------------------------------------
48−buildPaginate :: MonadMetadata m
50− -> m Paginate
45+paginateNumPages :: Paginate -> Int
51−buildPaginate pattern = do
46+paginateNumPages = M.size . paginateMap
52− idents <- getMatches pattern
53− let pagPages = M.fromList $ zip [1 ..] (map return idents)
54− pagPlaces = M.fromList $ zip idents [1 ..]
55− makeId pn = case M.lookup pn pagPages of
58− "Hakyll.Web.Paginate.buildPaginate: " ++
59− "invalid page number: " ++ show pn
61− return $ Paginate pagPages pagPlaces makeId
62− (PatternDependency pattern (S.fromList idents))
65--------------------------------------------------------------------------------
49--------------------------------------------------------------------------------
50+paginateEvery :: Int -> [a] -> [[a]]
54+ go xs = let (y, ys) = splitAt n xs in y : go ys
57+--------------------------------------------------------------------------------
66−buildPaginateWith :: MonadMetadata m
59+ :: MonadMetadata m
67− => Int
60+ => ([Identifier] -> m [[Identifier]]) -- ^ Group items into pages
68− -> (PageNumber -> Identifier)
69− -> Pattern
61+ -> Pattern -- ^ Select items to paginate
62+ -> (PageNumber -> Identifier) -- ^ Identifiers for the pages
70− -> m Paginate
63+ -> m Paginate
71−buildPaginateWith n makeId pattern = do
64+buildPaginateWith grouper pattern makeId = do
72− -- TODO: there is no sensible order for `ids` here, for now it's random;
73− -- but it should be `recentFirst` order because most recent posts should
74− -- correspond to 1st paginator page and oldest one to last page
75− idents <- getMatches pattern
65+ ids <- getMatches pattern
76− let pages = flip unfoldr idents $ \xs ->
66+ idGroups <- grouper ids
77− if null xs then Nothing else Just (splitAt n xs)
67+ let idsSet = S.fromList ids
78− nPages = length pages
68+ return Paginate
79− paginatePages' = zip [1..] pages
69+ { paginateMap = M.fromList (zip [1 ..] idGroups)
80− pagPlaces' =
70+ , paginateMakeId = makeId
81− [(ident, idx) | (idx,ids) <- paginatePages', ident <- ids] ++
82− [(makeId i, i) | i <- [1 .. nPages]]
84− return $ Paginate (M.fromList paginatePages') (M.fromList pagPlaces') makeId
85− (PatternDependency pattern (S.fromList idents))
71+ , paginateDependency = PatternDependency pattern idsSet
88--------------------------------------------------------------------------------
75--------------------------------------------------------------------------------
89paginateRules :: Paginate -> (PageNumber -> Pattern -> Rules ()) -> Rules ()
76paginateRules :: Paginate -> (PageNumber -> Pattern -> Rules ()) -> Rules ()
90paginateRules paginator rules =
77paginateRules paginator rules =
91− forM_ (M.toList $ paginatePages paginator) $ \(idx, identifiers) ->
78+ forM_ (M.toList $ paginateMap paginator) $ \(idx, identifiers) ->
92 rulesExtraDependencies [paginateDependency paginator] $
79 rulesExtraDependencies [paginateDependency paginator] $
93 create [paginateMakeId paginator idx] $
80 create [paginateMakeId paginator idx] $
94 rules idx $ fromList identifiers
81 rules idx $ fromList identifiers
97--------------------------------------------------------------------------------
84--------------------------------------------------------------------------------
98−-- | Takes first, current, last page and produces index of next page
85+-- | Get the identifier for a certain page by passing in the page number.
99−type RelPage = PageNumber -> PageNumber -> PageNumber -> Maybe PageNumber
86+paginatePage :: Paginate -> PageNumber -> Maybe Identifier
87+paginatePage pag pageNumber
88+ | pageNumber < 1 = Nothing
89+ | pageNumber > (paginateNumPages pag) = Nothing
90+ | otherwise = Just $ paginateMakeId pag pageNumber
102--------------------------------------------------------------------------------
93--------------------------------------------------------------------------------
103−paginateField :: Paginate -> String -> RelPage -> Context a
94+-- | A default paginate context which provides the following keys:
104−paginateField pag fieldName relPage = field fieldName $ \item ->
95+--
105− let identifier = itemIdentifier item
96+--
106− in case M.lookup identifier (paginatePlaces pag) of
97+paginateContext :: Paginate -> PageNumber -> Context a
107− Nothing -> fail $ printf
108− "Hakyll.Web.Paginate: there is no page %s in paginator map."
110− Just pos -> case relPage 1 pos nPages of
111− Nothing -> fail "Hakyll.Web.Paginate: No page here."
113− let nextId = paginateMakeId pag pos'
114− mroute <- getRoute nextId
116− Nothing -> fail $ printf
117− "Hakyll.Web.Paginate: unable to get route for %s."
119− Just rt -> return $ toUrl rt
121− nPages = M.size (paginatePages pag)
124−--------------------------------------------------------------------------------
125−paginateContext :: Paginate -> Context a
126−paginateContext pag = mconcat
98+paginateContext pag currentPage = mconcat
127− [ paginateField pag "firstPage"
99+ [ field "firstPageNum" $ \_ -> otherPage 1 >>= num
128− (\f c _ -> if c <= f then Nothing else Just f)
100+ , field "firstPageUrl" $ \_ -> otherPage 1 >>= url
129− , paginateField pag "previousPage"
101+ , field "previousPageNum" $ \_ -> otherPage (currentPage - 1) >>= num
130− (\f c _ -> if c <= f then Nothing else Just (c - 1))
102+ , field "previousPageUrl" $ \_ -> otherPage (currentPage - 1) >>= url
131− , paginateField pag "nextPage"
103+ , field "nextPageNum" $ \_ -> otherPage (currentPage + 1) >>= num
132− (\_ c l -> if c >= l then Nothing else Just (c + 1))
104+ , field "nextPageUrl" $ \_ -> otherPage (currentPage + 1) >>= url
133− , paginateField pag "lastPage"
105+ , field "lastPageNum" $ \_ -> otherPage lastPage >>= num
134− (\_ c l -> if c >= l then Nothing else Just l)
106+ , field "lastPageUrl" $ \_ -> otherPage lastPage >>= url
107+ , field "currentPageNum" $ \i -> thisPage i >>= num
108+ , field "currentPageUrl" $ \i -> thisPage i >>= url
109+ , constField "numPages" $ show $ paginateNumPages pag
112+ lastPage = paginateNumPages pag
114+ thisPage i = return (currentPage, itemIdentifier i)
116+ | n == currentPage = fail $ "This is the current page: " ++ show n
117+ | otherwise = case paginatePage pag n of
118+ Nothing -> fail $ "No such page: " ++ show n
119+ Just i -> return (n, i)
121+ num :: (Int, Identifier) -> Compiler String
122+ num = return . show . fst
124+ url :: (Int, Identifier) -> Compiler String
125+ url (n, i) = getRoute i >>= \mbR -> case mbR of
126+ Just r -> return $ toUrl r
127+ Nothing -> fail $ "No URL for page: " ++ show n