1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
-- | A Module that allows easy rendering of RSS feeds. If you use this module,
-- you must make sure you set the `absoluteUrl` field in the main Hakyll
-- configuration.
--
-- Apart from that, the main rendering functions (@renderRss@,
-- @renderRssWith@, @renderAtom@ all @renderAtomWith@) all assume that you
-- pass the list of @Renderable@s so that the most recent entry in the feed is
-- the first item in the list.
--
-- Also note that the @Renderable@s should have (at least) the following
-- fields to produce a correct feed:
--
-- - @$title@: Title of the item.
--
-- - @$description@: Description to appear in the feed.
--
-- - @$url@: URL to the item - this is usually set automatically.
--
-- Furthermore, the feed will not validate if an empty list is passed.
module Text.Hakyll.Feed
( FeedConfiguration (..)
, renderRss
, renderRssWith
, renderAtom
, renderAtomWith
) where
import Control.Arrow ((>>>), second)
import Control.Monad.Reader (liftIO)
import Data.Maybe (fromMaybe)
import qualified Data.Map as M
import Text.Hakyll.Context (ContextManipulation, renderDate)
import Text.Hakyll.Hakyll (Hakyll)
import Text.Hakyll.Render (render, renderChain)
import Text.Hakyll.Renderables (createListingWith)
import Text.Hakyll.RenderAction
import Paths_hakyll
-- | This is a data structure to keep the configuration of a feed.
data FeedConfiguration = FeedConfiguration
{ -- | Url of the feed (relative to site root). For example, @rss.xml@.
feedUrl :: String
, -- | Title of the feed.
feedTitle :: String
, -- | Description of the feed.
feedDescription :: String
, -- | Name of the feed author.
feedAuthorName :: String
}
-- | This is an auxiliary function to create a listing that is, in fact, a feed.
-- The items should be sorted on date.
createFeedWith :: ContextManipulation -- ^ Manipulation to apply on the items.
-> FeedConfiguration -- ^ Feed configuration.
-> [Renderable] -- ^ Items to include.
-> FilePath -- ^ Feed template.
-> FilePath -- ^ Item template.
-> Renderable
createFeedWith manipulation configuration renderables template itemTemplate =
listing >>> render template
where
listing = createListingWith manipulation (feedUrl configuration)
[itemTemplate] renderables additional
additional = map (second $ Left . ($ configuration))
[ ("title", feedTitle)
, ("description", feedDescription)
, ("authorName", feedAuthorName)
] ++ updated
-- Take the first timestamp, which should be the most recent.
updated = let action = createRenderAction $
return . fromMaybe "foo" . M.lookup "timestamp"
manip = createManipulationAction manipulation
toTuple r = ("timestamp", Right $ r >>> manip >>> action)
in map toTuple $ take 1 renderables
-- | Abstract function to render any feed.
renderFeedWith :: ContextManipulation -- ^ Manipulation to apply on the items.
-> FeedConfiguration -- ^ Feed configuration.
-> [Renderable] -- ^ Items to include in the feed.
-> FilePath -- ^ Feed template.
-> FilePath -- ^ Item template.
-> Hakyll ()
renderFeedWith manipulation configuration renderables template itemTemplate = do
template' <- liftIO $ getDataFileName template
itemTemplate' <- liftIO $ getDataFileName itemTemplate
let renderFeedWith' = createFeedWith manipulation configuration
renderables template' itemTemplate'
renderChain [] renderFeedWith'
-- | Render an RSS feed with a number of items.
renderRss :: FeedConfiguration -- ^ Feed configuration.
-> [Renderable] -- ^ Items to include in the RSS feed.
-> Hakyll ()
renderRss = renderRssWith id
-- | Render an RSS feed with a number of items. This function allows you to
-- specify a @ContextManipulation@ which will be applied on every
-- @Renderable@. Note that the given @Renderable@s should be sorted so the
-- most recent one is first.
renderRssWith :: ContextManipulation -- ^ Manipulation to apply on the items.
-> FeedConfiguration -- ^ Feed configuration.
-> [Renderable] -- ^ Items to include in the feed.
-> Hakyll ()
renderRssWith manipulation configuration renderables =
renderFeedWith manipulation' configuration renderables
"templates/rss.xml" "templates/rss-item.xml"
where
manipulation' = manipulation . renderRssDate
-- | @ContextManipulation@ that renders a date to RSS format.
renderRssDate :: ContextManipulation
renderRssDate = renderDate "timestamp" "%a, %d %b %Y %H:%M:%S UT"
"No date found."
-- | Render an Atom feed with a number of items.
renderAtom :: FeedConfiguration
-> [Renderable]
-> Hakyll ()
renderAtom = renderAtomWith id
-- | A version of @renderAtom@ that allows you to specify a manipulation to
-- apply on the @Renderable@s.
renderAtomWith :: ContextManipulation -- ^ Manipulation to apply on the items.
-> FeedConfiguration -- ^ Feed configuration.
-> [Renderable] -- ^ Items to include in the feed.
-> Hakyll ()
renderAtomWith manipulation configuration renderables =
renderFeedWith manipulation' configuration renderables
"templates/atom.xml" "templates/atom-item.xml"
where
manipulation' = manipulation . renderAtomDate
-- | Render a date to Atom format.
renderAtomDate :: ContextManipulation
renderAtomDate = renderDate "timestamp" "%Y-%m-%dT%H:%M:%SZ" "No date found."
|