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