summaryrefslogtreecommitdiff
path: root/examples/hakyll/tutorials/part03.markdown
blob: 3b04f9a2dfafacb4339ae1b06229494fd2dd5683 (plain)
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
---
title: How to write pages
what: elaborates a little on writing pages and templates
---

## The structure of a Page

The most important thing to realize is that a page is reduced to a `Context`,
and therefore is just a key-value mapping. Another example:

    ---
    title: About
    author: Mia Wallace
    ---
    Hello there! This is
    a simple about page.

This will produce the following mapping:

- `$title`: About
- `$author`: Mia Wallace
- `$body`: Hello there! This is a simple about page.

`$body` is the traditional name for the main body part of a page. If the page
has a `.markdown` extension for example, this would also be rendered by pandoc.
But pages are more flexible. The following is also a valid page:

    Hello there! This is
    a simple about page.

This will produce one key-value pair:

- `$body`: Hello there! This is a simple about page.

But Hakyll can do more than this. You can add extra sections, apart from the
body, and even leave out the body.

    ---
    author: Vincent Vega
   
    --- prelude
    A small introduction goes here. I can write *markdown*
    here, by the way. Well, assuming this page has a
    `.markdown` extension.
   
    --- main
    I can write some more things here.

    ---
    The body comes last, and is optional.

This will produce the following:

- `$author`: Vincent Vega
- `$prelude`: A small introduction goes here. I can write *markdown* here, by the
  way. Well, assuming this page has a `.markdown` extension.
- `$main`: I can write some more things here.
- `$body`: The body comes last, and is optional.

The example from this tutorial (we will see later) uses this to build a
three-column system for the website, separating content from layout.

## Combining Contexts

Now you know that pages, and `Context`s in general, are basically nothing more
than key-values mappings, it is time to abuse this fact. There is another
way to create a `Context`, called `combine`.

The type signature of the `combine` function does a pretty good job at
explaining it:

~~~~~{.haskell}
combine :: HakyllAction () Context
        -> HakyllAction () Context
        -> HakyllAction () Context
~~~~~

This means we can take two `Context`s values and combine them. This is
basically a `Map.union`: The result will contain all keys from both `Context`s,
with there corresponding values. If a key is present in both `Context`s, the
value from the first argument will be chosen. This is, for example, almost
always the case with the `$url` field (since almost all `Context`s have an url
in Hakyll).

Combining two `Context`s, but overriding the `$url` is quite common, so there is
another function that helps us here:

~~~~~{.haskell}
combineWithUrl :: FilePath
               -> HakyllAction () Context
               -> HakyllAction () Context
               -> HakyllAction () Context
~~~~~

## The example

Now that we have the tools, we'll get on to the example. This time, we'll
be making a more advanced brochure site. Here [is a zip file] containing the
source code for the tutorial.

[is a zip file]: $root/examples/morepages.zip

Every page consists of three sections, originally named `section1`, `section2`
and `section3`. So our pages look more or less like this:

    ---
    title: About

    --- section1
    ## Mattis
    Nullam imperdiet sodales orci vitae molestie. Nunc...

    --- section2
    ## Orci
    Vivamus eget mauris sit amet nulla laoreet lobortis.
    Nulla in...

    --- section3
    ## Augue
    In urna ante, pulvinar et imperdiet nec, fermentum ac...

The cool thing is we do not have to specify how these will be layed out. In our
template, we decide to use a simple three column system:

~~~~~{.html}
<div class="column"> $section1 </div>
<div class="column"> $section2 </div>
<div class="column"> $section3 </div>
~~~~~

The columns are then floated using css. So far so good, but what if we wanted
an additional text block on every page? An easy solution would be to add this
to the template, but then our layout-content separation idea will be broken
again. So we simply add to the template:

~~~~~{.html}
<div class="footer"> $footer </div>
~~~~~

And now we will use `combine` to put the footer on every page - so we need to
add the footer page to every `Context`. We write a small auxiliary function
that combines a given `Context` with the footer:

~~~~~{.haskell}
withFooter = flip combine $ createPage "footer.markdown"
~~~~~

Note that we use `flip` here - we want `footer.markdown` to be our second
argument. That is because Hakyll will take the `$url` from the first `Context`,
so all pages would be rendered to `footer.html` - obviously not what we want.
Now, were we previously wrote:

~~~~~{.haskell}
render "about.markdown"
where render = renderChain ["templates/default.html"]
             . createPage
~~~~~

We simply have to add our footer:

~~~~~{.haskell}
render "about.markdown"
where render = renderChain ["templates/default.html"]
             . withFooter
             . createPage
~~~~~

And now every page will include the footer.

## The gist of it

- Pages are just key-value mappings.
- You can have multiple sections in every page.
- Combine pages using the `combine` function.