@@ -2,6 +2,160 @@
22title: Tutorial (Part II)
33---
44
5−## Under construction
5+## Creating a simple blog with Hakyll
66
7−Coming soon.
7+After we created a simple brochure site, we're going to try something more
8+advanced: we are going to create a simple blog system.
9+
10+A [zip file containing the source](examples/simpleblog.zip) for this
11+tutorial is also available.
12+
13+Blogs, as you probably know, are composed of posts. In Hakyll, we're going
14+to use simple pages for posts. All posts are located in the `posts`
15+directory. But we're not going to use the `directory` command here - you will
16+see why later. First, some trivial things like css.
17+
18+~~~~~{.haskell}
19+main = hakyll $ do
20+ directory css "css"
21+~~~~~
22+
23+## Finding the posts, and a bit about renderables
24+
25+`Text.Hakyll.File` contains a handy function `getRecursiveContents`, which will
26+provide us with all the blog posts. The blog posts have a
27+`yyyy-mm-dd-title.extension` naming scheme. This is just a simple trick so we
28+can sort them easily, you could of course name them whatever you want. They
29+contain some metadata, too:
30+
31+~~~~~{.markdown}
32+---
33+title: A first post
34+author: Julius Caesar
35+date: November 5, 2009
36+---
37+Lorem ipsum dolor sit amet, consectetur adipiscing elit.
38+Vivamus pretium leo adipiscing lectus iaculis lobortis.
39+Vivamus scelerisque velit dignissim metus...
40+~~~~~
41+
42+Now, we find the posts and sort them reversed:
43+
44+~~~~~{.haskell}
45+-- Find all post paths.
46+postPaths <- liftM (reverse . sort) $ getRecursiveContents "posts"
47+~~~~~
48+
49+Our `postPaths` value is now of the type `[FilePath]`. `FilePath` is no
50+instance of `Renderable`, but `PagePath` is:
51+
52+~~~~~{.haskell}
53+let renderablePosts = map createPagePath postPaths
54+~~~~~
55+
56+We have two templates we want to render our posts with: first we would like to
57+render them using `templates/post.html`, and we want to render the result
58+using `templates/default.html`. This can be done with the `renderChain`
59+function:
60+
61+~~~~~{.haskell}
62+mapM_ (renderChain [ "templates/post.html"
63+ , "templates/default.html"
64+ ]) renderablePosts
65+~~~~~
66+
67+Remember that the `renderChain` works by rendering the datatype using the first
68+template, creating a new page with the render result in the `body` field, and so
69+on until it has been rendered with all templates.
70+
71+Now, we have the posts rendered. What is left is to generate some kind of index
72+page with links to those posts. We want one general list showing all posts, and
73+we want to show a few recent posts on the index page.
74+
75+## Custom Pages
76+
77+Currently, there are 3 renderable datatypes in Hakyll:
78+
79+- `Page`: The result of any rendering action. It is generally not recommended
80+ to use pages a lot, because they cannot check dependencies (and therefore,
81+ you would always regenerate your entire site if you use pages the wrong way).
82+- `PagePath`: Basically just a `FilePath` in a box. Internally, this will use
83+ a `Page` for rendering, but `PagePath` provides better dependency checking
84+ and works on a higher level.
85+- `CustomPage`: Basically the name says it - the preferred way of creating
86+ custom pages in Hakyll.
87+
88+We will use a `CustomPage` here. Basically, all `Renderable` datatypes are in
89+the end just `key: value` mappings. A CustomPage is created using the
90+`createCustomPage` function, which has the following type signature:
91+
92+~~~~~{.haskell}
93+createCustomPage :: FilePath
94+ -> [FilePath]
95+ -> [(String, Either String (IO ByteString)]
96+~~~~~
97+
98+The first argument is the `url` of the page to generate. For our index page,
99+this will be, `index.html`. The second argument is _a list of dependencies_.
100+Basically, you should here give a list of files on which your custom page
101+depends.
102+
103+The last argument is obviously our `key: value` mapping. But why the `Either`?
104+This, once again, is about dependency handling. The idea is that you can choose
105+which type to use for the value:
106+
107+- `String`: Simply a `String`.
108+- `IO ByteString`: Here, you can give an arbitrary `IO` action that will result
109+ in a ByteString. However - this action _will not be executed_ when the file
110+ in `_site` is up-to-date.
111+
112+First, let us define this `IO ByteString` for our index page. We want to render
113+every post using a simple template:
114+
115+~~~~~{.html}
116+<li>
117+ <a href="/$url">$title</a>
118+ - <em>$date</em> - by <em>$author</em>
119+</li>
120+~~~~~
121+
122+When every post is rendered with this template, we then want to concatenate the
123+result. Since rendering and concatenating is pretty common, Hakyll provides us
124+with a high-level function to do this.
125+
126+~~~~~{.haskell}
127+let recentPosts = renderAndConcat "templates/postitem.html"
128+ (take 3 renderablePosts)
129+~~~~~
130+
131+Now, creating our custom page is fairly straight-forward:
132+
133+~~~~~{.haskell}
134+createCustomPage "index.html"
135+ ("templates/postitem.html" : take 3 postPaths)
136+ [ ("title", Left "All posts")
137+ , ("posts", Right recentPosts)
138+ ]
139+~~~~~
140+
141+You can see our three arguments here. We're rendering `index.html`, then we tell
142+Hakyll on what files it depends - here the `templates/postitem.html` template
143+and the latest 3 posts. Finally, we give a `title` value to substitute in the
144+template, and the result of our concatenation. Of course, we also need to render
145+this custom page:
146+
147+~~~~~{.haskell}
148+renderChain ["index.html", "templates/default.html"] $
149+ createCustomPage "index.html"
150+ ("templates/postitem.html" : take 3 postPaths)
151+ [ ("title", Left "All posts")
152+ , ("posts", Right recentPosts)
153+ ]
154+~~~~~
155+
156+Note that the `index.html` in the `renderChain` list is also a template.
157+
158+## That's that
159+
160+If you have any more questions, feel free to ask them on the
161+[google discussion group](http://groups.google.com/group/hakyll).