@@ -0,0 +1,122 @@
1+-- | A Module that allows easy rendering of RSS feeds.
2+--
3+-- The main rendering functions (@renderRss@, @renderAtom@) all assume that
4+-- you pass the list of items so that the most recent entry in the feed is the
5+-- first item in the list.
6+--
7+-- Also note that the pages should have (at least) the following fields to
8+-- produce a correct feed:
9+--
10+-- - @$title@: Title of the item
11+--
12+-- - @$description@: Description to appear in the feed
13+--
14+-- - @$url@: URL to the item - this is usually set automatically.
15+--
16+-- In addition, the posts should be named according to the rules for
17+-- 'Hakyll.Page.Metadata.renderDateField'.
18+--
19+module Hakyll.Web.Feed
20+ ( FeedConfiguration (..)
21+ , renderRss
22+ , renderAtom
23+ ) where
24+
25+import Prelude hiding (id)
26+import Control.Category (id)
27+import Control.Arrow ((>>>), arr, (&&&))
28+import Control.Monad ((<=<))
29+import Data.Maybe (fromMaybe, listToMaybe)
30+
31+import Hakyll.Core.Compiler
32+import Hakyll.Web.Page
33+import Hakyll.Web.Page.Metadata
34+import Hakyll.Web.Template
35+
36+import Paths_hakyll
37+
38+-- | This is a data structure to keep the configuration of a feed.
39+data FeedConfiguration = FeedConfiguration
40+ { -- | Title of the feed.
41+ feedTitle :: String
42+ , -- | Description of the feed.
43+ feedDescription :: String
44+ , -- | Name of the feed author.
45+ feedAuthorName :: String
46+ , -- | Absolute root URL of the feed site (e.g. @http://jaspervdj.be@)
47+ feedRoot :: String
48+ }
49+
50+-- | This is an auxiliary function to create a listing that is, in fact, a feed.
51+-- The items should be sorted on date. The @$timestamp@ field should be set.
52+--
53+createFeed :: Template -- ^ Feed template
54+ -> Template -- ^ Item template
55+ -> String -- ^ URL of the feed
56+ -> FeedConfiguration -- ^ Feed configuration
57+ -> [Page String] -- ^ Items to include
58+ -> String -- ^ Resulting feed
59+createFeed feedTemplate itemTemplate url configuration items =
60+ pageBody $ applyTemplate feedTemplate
61+ $ setField "timestamp" timestamp
62+ $ setField "title" (feedTitle configuration)
63+ $ setField "description" (feedDescription configuration)
64+ $ setField "authorName" (feedDescription configuration)
65+ $ setField "root" (feedRoot configuration)
66+ $ setField "url" url
67+ $ fromBody body
68+ where
69+ -- Preprocess items
70+ items' = flip map items $ applyTemplate itemTemplate
71+ . setField "root" (feedRoot configuration)
72+
73+ -- Body: concatenated items
74+ body = concat $ map pageBody items'
75+
76+ -- Take the first timestamp, which should be the most recent
77+ timestamp = fromMaybe "Unknown" $ do
78+ p <- listToMaybe items
79+ return $ getField "timestamp" p
80+
81+
82+-- | Abstract function to render any feed.
83+--
84+renderFeed :: FilePath -- ^ Feed template
85+ -> FilePath -- ^ Item template
86+ -> FeedConfiguration -- ^ Feed configuration
87+ -> Compiler [Page String] String -- ^ Feed compiler
88+renderFeed feedTemplate itemTemplate configuration =
89+ id &&& getRoute >>> renderFeed'
90+ where
91+ -- Arrow rendering the feed from the items and the URL
92+ renderFeed' = unsafeCompiler $ \(items, url) -> do
93+ feedTemplate' <- loadTemplate feedTemplate
94+ itemTemplate' <- loadTemplate itemTemplate
95+ let url' = fromMaybe noUrl url
96+ return $ createFeed feedTemplate' itemTemplate' url' configuration items
97+
98+ -- Auxiliary: load a template from a datafile
99+ loadTemplate = fmap readTemplate . readFile <=< getDataFileName
100+
101+ -- URL is required to have a valid field
102+ noUrl = error "Hakyll.Web.Feed.renderFeed: no route specified"
103+
104+-- | Render an RSS feed with a number of items.
105+--
106+renderRss :: FeedConfiguration -- ^ Feed configuration
107+ -> Compiler [Page String] String -- ^ Feed compiler
108+renderRss configuration = arr (map renderDate)
109+ >>> renderFeed "templates/rss.xml" "templates/rss-item.xml" configuration
110+ where
111+ renderDate = renderDateField "timestamp" "%a, %d %b %Y %H:%M:%S UT"
112+ "No date found."
113+
114+-- | Render an Atom feed with a number of items.
115+--
116+renderAtom :: FeedConfiguration -- ^ Feed configuration
117+ -> Compiler [Page String] String -- ^ Feed compiler
118+renderAtom configuration = arr (map renderDate)
119+ >>> renderFeed "templates/atom.xml" "templates/atom-item.xml" configuration
120+ where
121+ renderDate = renderDateField "timestamp" "%Y-%m-%dT%H:%M:%SZ"
122+ "No date found."