Wrote another tutorial, and updated some tutorials.
This commit is contained in:
parent
db0eef8470
commit
0ba0eac8d9
12 changed files with 711 additions and 400 deletions
|
@ -66,12 +66,12 @@ metadata. The metadata is placed in the file header and surrouded by `---`
|
|||
lines. Each line should contain a `key: value` pair. Let's have a look at the
|
||||
`about.markdown` page.
|
||||
|
||||
> ---
|
||||
> title: About
|
||||
> ---
|
||||
> Nullam imperdiet sodales orci vitae molestie.
|
||||
> Nunc quam orci, pharetra a rhoncus vitae,
|
||||
> eleifend id felis. Suspendisse potenti...
|
||||
---
|
||||
title: About
|
||||
---
|
||||
Nullam imperdiet sodales orci vitae molestie.
|
||||
Nunc quam orci, pharetra a rhoncus vitae,
|
||||
eleifend id felis. Suspendisse potenti...
|
||||
|
||||
This contains one `key: value` pair, namely `title: About`. The rest of the
|
||||
file is treated as markdown by pandoc. If you want to know more about
|
||||
|
@ -209,4 +209,4 @@ questions, feel free to ask them on the
|
|||
[google discussion group](http://groups.google.com/group/hakyll).
|
||||
|
||||
If you feel comfortable with the basics, here is a next tutorial:
|
||||
[building a simple blog](tutorial2.html).
|
||||
[more about writing pages](tutorial2.html).
|
||||
|
|
|
@ -1,162 +1,159 @@
|
|||
---
|
||||
title: Tutorial (Part II)
|
||||
what: creates a simple blog
|
||||
what: elaborates a little on writing pages and templates
|
||||
---
|
||||
|
||||
## Creating a simple blog with Hakyll
|
||||
## The structure of a Page
|
||||
|
||||
After we created a simple brochure site, we're going to try something more
|
||||
advanced: we are going to create a simple blog system.
|
||||
The most important thing to realize is that a `Page` is just a key-value
|
||||
mapping. Another example:
|
||||
|
||||
A [zip file containing the source](examples/simpleblog.zip) for this
|
||||
tutorial is also available.
|
||||
---
|
||||
title: About
|
||||
author: Mia Wallace
|
||||
---
|
||||
Hello there! This is
|
||||
a simple about page.
|
||||
|
||||
Blogs, as you probably know, are composed of posts. In Hakyll, we're going
|
||||
to use simple pages for posts. All posts are located in the `posts`
|
||||
directory. But we're not going to use the `directory` command here - you will
|
||||
see why later. First, some trivial things like css.
|
||||
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 the `Page` parser 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.
|
||||
|
||||
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.
|
||||
|
||||
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 pages
|
||||
|
||||
Now you know that `Page`s, and `Renderable`s in general, are basically nothing
|
||||
more than key-values mappings, it is time to abuse this fact. There is another
|
||||
`Renderable` type we haven't talked about before: a `CombinedRenderable`.
|
||||
|
||||
The type signature of the `combine` function does a pretty good job at
|
||||
explaining it:
|
||||
|
||||
~~~~~{.haskell}
|
||||
main = hakyll $ do
|
||||
directory css "css"
|
||||
combine :: (Renderable a, Renderable b)
|
||||
=> a -> b -> CombinedRenderable a b
|
||||
~~~~~
|
||||
|
||||
## Finding the posts, and a bit about renderables
|
||||
This means we can take two `Renderable` values and combine them. This is
|
||||
basically a `Map.union`: The result will contain all keys from `a`, and all
|
||||
keys from `b`. If a key is present in both `Renderable`s, the value from `a`
|
||||
will be chosen. This is, for example, always the case with an `url` (since
|
||||
all `Renderable` types always have an url).
|
||||
|
||||
`Text.Hakyll.File` contains a handy function `getRecursiveContents`, which will
|
||||
provide us with all the blog posts. The blog posts have a
|
||||
`yyyy-mm-dd-title.extension` naming scheme. This is just a simple trick so we
|
||||
can sort them easily, you could of course name them whatever you want. They
|
||||
contain some metadata, too:
|
||||
|
||||
> title: A first post
|
||||
> author: Julius Caesar
|
||||
> date: November 5, 2009
|
||||
> ---
|
||||
> Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
> Vivamus pretium leo adipiscing lectus iaculis lobortis.
|
||||
> Vivamus scelerisque velit dignissim metus...
|
||||
|
||||
Now, we find the posts and sort them reversed:
|
||||
Combining two `Renderable`s, but setting a different `url` is quite common, so
|
||||
there is another function that helps us here:
|
||||
|
||||
~~~~~{.haskell}
|
||||
-- Find all post paths.
|
||||
postPaths <- liftM (reverse . sort) $ getRecursiveContents "posts"
|
||||
combineWithURL :: (Renderable a, Renderable b)
|
||||
=> FilePath -> a -> b -> CombinedRenderable a b
|
||||
~~~~~
|
||||
|
||||
Our `postPaths` value is now of the type `[FilePath]`. `FilePath` is no
|
||||
instance of `Renderable`, but `PagePath` is:
|
||||
## The example
|
||||
|
||||
~~~~~{.haskell}
|
||||
let renderablePosts = map createPagePath postPaths
|
||||
~~~~~
|
||||
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](examples/morepages.zip) containing the source code for the
|
||||
tutorial.
|
||||
|
||||
We have two templates we want to render our posts with: first we would like to
|
||||
render them using `templates/post.html`, and we want to render the result
|
||||
using `templates/default.html`. This can be done with the `renderChain`
|
||||
function:
|
||||
Every page consists of three sections, originally named `section1`, `section2`
|
||||
and `section3`. So our pages look more or less like this:
|
||||
|
||||
~~~~~{.haskell}
|
||||
mapM_ (renderChain [ "templates/post.html"
|
||||
, "templates/default.html"
|
||||
]) renderablePosts
|
||||
~~~~~
|
||||
---
|
||||
title: About
|
||||
|
||||
Remember that the `renderChain` works by rendering the datatype using the first
|
||||
template, creating a new page with the render result in the `body` field, and so
|
||||
on until it has been rendered with all templates.
|
||||
--- section1
|
||||
## Mattis
|
||||
Nullam imperdiet sodales orci vitae molestie. Nunc...
|
||||
|
||||
Now, we have the posts rendered. What is left is to generate some kind of index
|
||||
page with links to those posts. We want one general list showing all posts, and
|
||||
we want to show a few recent posts on the index page.
|
||||
--- section2
|
||||
## Orci
|
||||
Vivamus eget mauris sit amet nulla laoreet lobortis. Nulla in...
|
||||
|
||||
## Custom Pages
|
||||
--- section3
|
||||
## Augue
|
||||
In urna ante, pulvinar et imperdiet nec, fermentum ac...
|
||||
|
||||
Currently, there are 3 renderable datatypes in Hakyll:
|
||||
|
||||
- `Page`: The result of any rendering action. It is generally not recommended
|
||||
to use pages a lot, because they cannot check dependencies (and therefore,
|
||||
you would always regenerate your entire site if you use pages the wrong way).
|
||||
- `PagePath`: Basically just a `FilePath` in a box. Internally, this will use
|
||||
a `Page` for rendering, but `PagePath` provides better dependency checking
|
||||
and works on a higher level.
|
||||
- `CustomPage`: Basically the name says it - the preferred way of creating
|
||||
custom pages in Hakyll.
|
||||
|
||||
We will use a `CustomPage` here. Basically, all `Renderable` datatypes are in
|
||||
the end just `key: value` mappings. A CustomPage is created using the
|
||||
`createCustomPage` function, which has the following type signature:
|
||||
|
||||
~~~~~{.haskell}
|
||||
createCustomPage :: FilePath
|
||||
-> [FilePath]
|
||||
-> [(String, Either String (IO String)]
|
||||
~~~~~
|
||||
|
||||
The first argument is the `url` of the page to generate. For our index page,
|
||||
this will be, `index.html`. The second argument is _a list of dependencies_.
|
||||
Basically, you should here give a list of files on which your custom page
|
||||
depends.
|
||||
|
||||
The last argument is obviously our `key: value` mapping. But why the `Either`?
|
||||
This, once again, is about dependency handling. The idea is that you can choose
|
||||
which type to use for the value:
|
||||
|
||||
- `String`: Simply a `String`.
|
||||
- `IO String`: Here, you can give an arbitrary `IO` action that will result
|
||||
in a String. However - this action _will not be executed_ when the file
|
||||
in `_site` is up-to-date.
|
||||
|
||||
First, let us define this `IO String` for our index page. We want to render
|
||||
every post using a simple template:
|
||||
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}
|
||||
<li>
|
||||
<a href="$$root/$url">$title</a>
|
||||
- <em>$date</em> - by <em>$author</em>
|
||||
</li>
|
||||
<div class="column"> $section1 </div>
|
||||
<div class="column"> $section2 </div>
|
||||
<div class="column"> $section3 </div>
|
||||
~~~~~
|
||||
|
||||
When every post is rendered with this template, we then want to concatenate the
|
||||
result. Since rendering and concatenating is pretty common, Hakyll provides us
|
||||
with a high-level function to do this.
|
||||
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. We write a small
|
||||
auxiliary function that combines a given `Renderable` with the footer:
|
||||
|
||||
~~~~~{.haskell}
|
||||
let recentPosts = renderAndConcat "templates/postitem.html"
|
||||
(take 3 renderablePosts)
|
||||
withFooter a = a `combine` createPagePath "footer.markdown"
|
||||
~~~~~
|
||||
|
||||
Now, creating our custom page is fairly straight-forward:
|
||||
Now, were we previously wrote:
|
||||
|
||||
~~~~~{.haskell}
|
||||
createCustomPage "index.html"
|
||||
("templates/postitem.html" : take 3 postPaths)
|
||||
[ ("title", Left "All posts")
|
||||
, ("posts", Right recentPosts)
|
||||
]
|
||||
render "about.markdown"
|
||||
where render = renderChain ["templates/default.html"]
|
||||
. createPagePath
|
||||
~~~~~
|
||||
|
||||
You can see our three arguments here. We're rendering `index.html`, then we tell
|
||||
Hakyll on what files it depends - here the `templates/postitem.html` template
|
||||
and the latest 3 posts. Finally, we give a `title` value to substitute in the
|
||||
template, and the result of our concatenation. Of course, we also need to render
|
||||
this custom page:
|
||||
We simply have to add our footer:
|
||||
|
||||
~~~~~{.haskell}
|
||||
renderChain ["index.html", "templates/default.html"] $
|
||||
createCustomPage "index.html"
|
||||
("templates/postitem.html" : take 3 postPaths)
|
||||
[ ("title", Left "All posts")
|
||||
, ("posts", Right recentPosts)
|
||||
]
|
||||
render "about.markdown"
|
||||
where render = renderChain ["templates/default.html"]
|
||||
. withFooter
|
||||
. createPagePath
|
||||
~~~~~
|
||||
|
||||
Note that the `index.html` in the `renderChain` list is also a template.
|
||||
And now every page will include the footer.
|
||||
|
||||
## That's that
|
||||
## That's all folks
|
||||
|
||||
If you have any more questions, feel free to ask them on the
|
||||
I hope this tutorial was clear enough to teach you the concepts of pages and
|
||||
combining renderables. As always, questions and feedback are welcome at the
|
||||
[google discussion group](http://groups.google.com/group/hakyll).
|
||||
|
||||
There is a [next tutorial](tutorial3.html), explaining how to add an RSS feed
|
||||
to our sample blog.
|
||||
|
|
|
@ -1,124 +1,162 @@
|
|||
---
|
||||
title: Tutorial (Part III)
|
||||
what: adds an RSS feed to the blog from the previous tutorial
|
||||
what: creates a simple blog
|
||||
---
|
||||
|
||||
## Adding RSS to our simple blog
|
||||
## Creating a simple blog with Hakyll
|
||||
|
||||
In this tutorial, we're going to add an RSS feed to the blog we wrote in
|
||||
[the previous tutorial](tutorial2.html). Here is a
|
||||
[zip file containing the source](examples/rssblog.zip).
|
||||
After we created a simple brochure site, we're going to try something more
|
||||
advanced: we are going to create a simple blog system.
|
||||
|
||||
An RSS feed looks like this:
|
||||
A [zip file containing the source](examples/simpleblog.zip) for this
|
||||
tutorial is also available.
|
||||
|
||||
~~~~~{.xml}
|
||||
<?xml version="1.0" ?>
|
||||
<rss version="2.0">
|
||||
<channel>
|
||||
<title>The SimpleBlog</title>
|
||||
<link>http://example.com/</link>
|
||||
<description>Simple blog in hakyll</description>
|
||||
<item>
|
||||
<title>Title goes here</title>
|
||||
<link>http://example.com/post.html</link>
|
||||
<description>
|
||||
A description is optional.
|
||||
</description>
|
||||
</item>
|
||||
</channel>
|
||||
</rss>
|
||||
~~~~~
|
||||
|
||||
Note that, obviously, there can be more than one item. We're going to use a
|
||||
template to render this. This is where `templates/rss.xml` comes in:
|
||||
|
||||
~~~~~{.xml}
|
||||
<?xml version="1.0" ?>
|
||||
<rss version="2.0">
|
||||
<channel>
|
||||
<title>The SimpleBlog</title>
|
||||
<link>http://jaspervdj.be/</link>
|
||||
<description>Simple blog in hakyll</description>
|
||||
$items
|
||||
</channel>
|
||||
</rss>
|
||||
~~~~~
|
||||
|
||||
We thus render our feed with the following code (no, I didn't define `rssPage`
|
||||
yet - I'm going to work from bottom to top here, it's easier to explain it
|
||||
that way).
|
||||
Blogs, as you probably know, are composed of posts. In Hakyll, we're going
|
||||
to use simple pages for posts. All posts are located in the `posts`
|
||||
directory. But we're not going to use the `directory` command here - you will
|
||||
see why later. First, some trivial things like css.
|
||||
|
||||
~~~~~{.haskell}
|
||||
renderChain ["templates/rss.xml"] rssPage
|
||||
main = hakyll $ do
|
||||
directory css "css"
|
||||
~~~~~
|
||||
|
||||
This, as you can see, is a regular render chain, once again. We need make a
|
||||
`Renderable` that "fills in" the `$items` identifier. We're going to do this
|
||||
using a [custom page](tutorial2.html#custom-pages).
|
||||
## Finding the posts, and a bit about renderables
|
||||
|
||||
## Custom pages again
|
||||
`Text.Hakyll.File` contains a handy function `getRecursiveContents`, which will
|
||||
provide us with all the blog posts. The blog posts have a
|
||||
`yyyy-mm-dd-title.extension` naming scheme. This is just a simple trick so we
|
||||
can sort them easily, you could of course name them whatever you want. They
|
||||
contain some metadata, too:
|
||||
|
||||
Note that we do not have to include all posts in the rss feed - only a few
|
||||
recent ones. We'll settle on the latest three here.
|
||||
title: A first post
|
||||
author: Julius Caesar
|
||||
date: November 5, 2009
|
||||
---
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
Vivamus pretium leo adipiscing lectus iaculis lobortis.
|
||||
Vivamus scelerisque velit dignissim metus...
|
||||
|
||||
We want to render every post using the following template,
|
||||
`templates/rssitem.xml`:
|
||||
Now, we find the posts and sort them reversed:
|
||||
|
||||
~~~~~{.haskell}
|
||||
<item>
|
||||
<title>$title</title>
|
||||
<link>http://example.com/$url</link>
|
||||
<description>$title by $author</description>
|
||||
</item>
|
||||
-- Find all post paths.
|
||||
postPaths <- liftM (reverse . sort) $ getRecursiveContents "posts"
|
||||
~~~~~
|
||||
|
||||
Since we build on the previous example, we still have our `renderablePosts`
|
||||
list. We'll be using it again:
|
||||
Our `postPaths` value is now of the type `[FilePath]`. `FilePath` is no
|
||||
instance of `Renderable`, but `PagePath` is:
|
||||
|
||||
~~~~~{.haskell}
|
||||
let recentRSSItems = renderAndConcat "templates/rssitem.xml"
|
||||
(take 3 renderablePosts)
|
||||
let renderablePosts = map createPagePath postPaths
|
||||
~~~~~
|
||||
|
||||
We're using the `renderAndConcat` function again. Note that because of
|
||||
hakyll/haskell laziness, this action isn't executed directly, and this helps
|
||||
dependency handling.
|
||||
|
||||
Now, the `rssPage` page. As you might remember, we use the `createCustomPage`
|
||||
function to create a custom page. We first give the destination url, then a
|
||||
list of dependencies, and then a list of `(key, value)` pairs.
|
||||
We have two templates we want to render our posts with: first we would like to
|
||||
render them using `templates/post.html`, and we want to render the result
|
||||
using `templates/default.html`. This can be done with the `renderChain`
|
||||
function:
|
||||
|
||||
~~~~~{.haskell}
|
||||
let rssPage = createCustomPage
|
||||
"rss.xml"
|
||||
("templates/postitem.html" : take 3 postPaths)
|
||||
[("items", Right recentRSSItems)]
|
||||
mapM_ (renderChain [ "templates/post.html"
|
||||
, "templates/default.html"
|
||||
]) renderablePosts
|
||||
~~~~~
|
||||
|
||||
## Adding a link to the feed
|
||||
Remember that the `renderChain` works by rendering the datatype using the first
|
||||
template, creating a new page with the render result in the `body` field, and so
|
||||
on until it has been rendered with all templates.
|
||||
|
||||
According to the w3 organization,
|
||||
[you should add \<link\> tags](http://www.w3.org/QA/Tips/use-links) to your
|
||||
documents, so we'll do this. In `templates/default.html`:
|
||||
Now, we have the posts rendered. What is left is to generate some kind of index
|
||||
page with links to those posts. We want one general list showing all posts, and
|
||||
we want to show a few recent posts on the index page.
|
||||
|
||||
~~~~~~{.html}
|
||||
<head>
|
||||
<title>SimpleBlog - $title</title>
|
||||
<link rel="stylesheet" type="text/css" href="$$root/css/default.css" />
|
||||
<link rel="alternate"
|
||||
type="application/rss+xml"
|
||||
title="SimpleBlog"
|
||||
href="http://example.com/rss.xml" />
|
||||
</head>
|
||||
~~~~~~
|
||||
## Custom Pages
|
||||
|
||||
This makes most browsers see the rss feed, and show it in the address bar.
|
||||
If you want, you can also add a pretty button to your blog linking to
|
||||
`rss.xml`.
|
||||
Currently, there are 3 renderable datatypes in Hakyll:
|
||||
|
||||
## That's it!
|
||||
- `Page`: The result of any rendering action. It is generally not recommended
|
||||
to use pages a lot, because they cannot check dependencies (and therefore,
|
||||
you would always regenerate your entire site if you use pages the wrong way).
|
||||
- `PagePath`: Basically just a `FilePath` in a box. Internally, this will use
|
||||
a `Page` for rendering, but `PagePath` provides better dependency checking
|
||||
and works on a higher level.
|
||||
- `CustomPage`: Basically the name says it - the preferred way of creating
|
||||
custom pages in Hakyll.
|
||||
|
||||
Yep, that's it. Feel free to play with the source code in
|
||||
[the zip file](examples/rssblog.zip) and extend the blog further. As always,
|
||||
all questions are welcome on the
|
||||
We will use a `CustomPage` here. Basically, all `Renderable` datatypes are in
|
||||
the end just `key: value` mappings. A CustomPage is created using the
|
||||
`createCustomPage` function, which has the following type signature:
|
||||
|
||||
~~~~~{.haskell}
|
||||
createCustomPage :: FilePath
|
||||
-> [FilePath]
|
||||
-> [(String, Either String (IO String)]
|
||||
~~~~~
|
||||
|
||||
The first argument is the `url` of the page to generate. For our index page,
|
||||
this will be, `index.html`. The second argument is _a list of dependencies_.
|
||||
Basically, you should here give a list of files on which your custom page
|
||||
depends.
|
||||
|
||||
The last argument is obviously our `key: value` mapping. But why the `Either`?
|
||||
This, once again, is about dependency handling. The idea is that you can choose
|
||||
which type to use for the value:
|
||||
|
||||
- `String`: Simply a `String`.
|
||||
- `IO String`: Here, you can give an arbitrary `IO` action that will result
|
||||
in a String. However - this action _will not be executed_ when the file
|
||||
in `_site` is up-to-date.
|
||||
|
||||
First, let us define this `IO String` for our index page. We want to render
|
||||
every post using a simple template:
|
||||
|
||||
~~~~~{.html}
|
||||
<li>
|
||||
<a href="$$root/$url">$title</a>
|
||||
- <em>$date</em> - by <em>$author</em>
|
||||
</li>
|
||||
~~~~~
|
||||
|
||||
When every post is rendered with this template, we then want to concatenate the
|
||||
result. Since rendering and concatenating is pretty common, Hakyll provides us
|
||||
with a high-level function to do this.
|
||||
|
||||
~~~~~{.haskell}
|
||||
let recentPosts = renderAndConcat ["templates/postitem.html"]
|
||||
(take 3 renderablePosts)
|
||||
~~~~~
|
||||
|
||||
Now, creating our custom page is fairly straight-forward:
|
||||
|
||||
~~~~~{.haskell}
|
||||
createCustomPage "index.html"
|
||||
("templates/postitem.html" : take 3 postPaths)
|
||||
[ ("title", Left "All posts")
|
||||
, ("posts", Right recentPosts)
|
||||
]
|
||||
~~~~~
|
||||
|
||||
You can see our three arguments here. We're rendering `index.html`, then we tell
|
||||
Hakyll on what files it depends - here the `templates/postitem.html` template
|
||||
and the latest 3 posts. Finally, we give a `title` value to substitute in the
|
||||
template, and the result of our concatenation. Of course, we also need to render
|
||||
this custom page:
|
||||
|
||||
~~~~~{.haskell}
|
||||
renderChain ["index.html", "templates/default.html"] $
|
||||
createCustomPage "index.html"
|
||||
("templates/postitem.html" : take 3 postPaths)
|
||||
[ ("title", Left "All posts")
|
||||
, ("posts", Right recentPosts)
|
||||
]
|
||||
~~~~~
|
||||
|
||||
Note that the `index.html` in the `renderChain` list is also a template.
|
||||
|
||||
## That's that
|
||||
|
||||
If you have any more questions, feel free to ask them on the
|
||||
[google discussion group](http://groups.google.com/group/hakyll).
|
||||
|
||||
There is a [next tutorial](tutorial4.html), explaining how to add an RSS feed
|
||||
to our sample blog.
|
||||
|
|
|
@ -1,211 +1,124 @@
|
|||
---
|
||||
title: Tutorial (Part IV)
|
||||
what: adds tags and context manipulations to our blog
|
||||
what: adds an RSS feed to the blog from the previous tutorial
|
||||
---
|
||||
|
||||
Here, have [a zip file](examples/tagblog.zip) for this tutorial.
|
||||
## Adding RSS to our simple blog
|
||||
|
||||
## Context manipulations
|
||||
In this tutorial, we're going to add an RSS feed to the blog we wrote in
|
||||
[the previous tutorial](tutorial2.html). Here is a
|
||||
[zip file containing the source](examples/rssblog.zip).
|
||||
|
||||
As you might remember, `Renderable` objects are usually just key-value mappings.
|
||||
We can render those with templates, and then the `$key`'s in the template get
|
||||
substituted by the appropriate values. This is a rather flexible system, but
|
||||
there are limitations. Some of these limitations can be solved using
|
||||
_Context Manipulations_.
|
||||
An RSS feed looks like this:
|
||||
|
||||
In `Text.Hakyll.Context`, we see the type of `ContextManipulation`:
|
||||
~~~~~{.xml}
|
||||
<?xml version="1.0" ?>
|
||||
<rss version="2.0">
|
||||
<channel>
|
||||
<title>The SimpleBlog</title>
|
||||
<link>http://example.com/</link>
|
||||
<description>Simple blog in hakyll</description>
|
||||
<item>
|
||||
<title>Title goes here</title>
|
||||
<link>http://example.com/post.html</link>
|
||||
<description>
|
||||
A description is optional.
|
||||
</description>
|
||||
</item>
|
||||
</channel>
|
||||
</rss>
|
||||
~~~~~
|
||||
|
||||
Note that, obviously, there can be more than one item. We're going to use a
|
||||
template to render this. This is where `templates/rss.xml` comes in:
|
||||
|
||||
~~~~~{.xml}
|
||||
<?xml version="1.0" ?>
|
||||
<rss version="2.0">
|
||||
<channel>
|
||||
<title>The SimpleBlog</title>
|
||||
<link>http://jaspervdj.be/</link>
|
||||
<description>Simple blog in hakyll</description>
|
||||
$items
|
||||
</channel>
|
||||
</rss>
|
||||
~~~~~
|
||||
|
||||
We thus render our feed with the following code (no, I didn't define `rssPage`
|
||||
yet - I'm going to work from bottom to top here, it's easier to explain it
|
||||
that way).
|
||||
|
||||
~~~~~{.haskell}
|
||||
type ContextManipulation = Context -> Context
|
||||
renderChain ["templates/rss.xml"] rssPage
|
||||
~~~~~
|
||||
|
||||
Where `Context` is a key-value mapping. Usually, you don't create
|
||||
`ContextManipulation`'s by hand, but you use the `renderValue` function. Let's
|
||||
have a look at it's type.
|
||||
This, as you can see, is a regular render chain, once again. We need make a
|
||||
`Renderable` that "fills in" the `$items` identifier. We're going to do this
|
||||
using a [custom page](tutorial2.html#custom-pages).
|
||||
|
||||
## Custom pages again
|
||||
|
||||
Note that we do not have to include all posts in the rss feed - only a few
|
||||
recent ones. We'll settle on the latest three here.
|
||||
|
||||
We want to render every post using the following template,
|
||||
`templates/rssitem.xml`:
|
||||
|
||||
~~~~~{.haskell}
|
||||
renderValue :: String -> String
|
||||
-> (String -> String)
|
||||
-> ContextManipulation
|
||||
<item>
|
||||
<title>$title</title>
|
||||
<link>http://example.com/$url</link>
|
||||
<description>$title by $author</description>
|
||||
</item>
|
||||
~~~~~
|
||||
|
||||
This is the preferred way of creating context manipulations. The first argument
|
||||
is the `key` to manipulate. The second argument is the `key` where the new value
|
||||
should be placed. If this is the same as the first argument, it will be
|
||||
replaced. The third argument is the function to manipulate the `value` with.
|
||||
|
||||
As a simple example, let's write a function that puts the `$title` in uppercase.
|
||||
Since we build on the previous example, we still have our `renderablePosts`
|
||||
list. We'll be using it again:
|
||||
|
||||
~~~~~{.haskell}
|
||||
import Data.Char (toUpper)
|
||||
|
||||
titleUpper :: ContextManipulation
|
||||
titleUpper = renderValue "title" "title" $ map toUpper
|
||||
let recentRSSItems = renderAndConcat ["templates/rssitem.xml"]
|
||||
(take 3 renderablePosts)
|
||||
~~~~~
|
||||
|
||||
## Applying Context Manipulations
|
||||
We're using the `renderAndConcat` function again. Note that because of
|
||||
hakyll/haskell laziness, this action isn't executed directly, and this helps
|
||||
dependency handling.
|
||||
|
||||
Now, the question is how to apply these `ContextManipulation`'s. The answer is
|
||||
simple. For every important render function (`render`, `renderChain`,
|
||||
`renderAndConcat`), there is a variant that takes a `ContextManipulation` as a
|
||||
first argument. These functions are thus: `renderWith`, `renderChainWith`,
|
||||
`renderAndConcatWith`. In fact, the following holds true:
|
||||
Now, the `rssPage` page. As you might remember, we use the `createCustomPage`
|
||||
function to create a custom page. We first give the destination url, then a
|
||||
list of dependencies, and then a list of `(key, value)` pairs.
|
||||
|
||||
~~~~~{.haskell}
|
||||
render == renderWith id
|
||||
renderChain == renderChainWith id
|
||||
renderAndConcat == renderAndConcatWith id
|
||||
let rssPage = createCustomPage
|
||||
"rss.xml"
|
||||
("templates/postitem.html" : take 3 postPaths)
|
||||
[("items", Right recentRSSItems)]
|
||||
~~~~~
|
||||
|
||||
So we could use or title manipulation like this:
|
||||
## Adding a link to the feed
|
||||
|
||||
~~~~~{.haskell}
|
||||
renderChainWith titleUpper ["templates/default.html"]
|
||||
(createPagePath "index.markdown")
|
||||
~~~~~
|
||||
According to the w3 organization,
|
||||
[you should add \<link\> tags](http://www.w3.org/QA/Tips/use-links) to your
|
||||
documents, so we'll do this. In `templates/default.html`:
|
||||
|
||||
## Rendering dates
|
||||
~~~~~~{.html}
|
||||
<head>
|
||||
<title>SimpleBlog - $title</title>
|
||||
<link rel="stylesheet" type="text/css" href="$$root/css/default.css" />
|
||||
<link rel="alternate"
|
||||
type="application/rss+xml"
|
||||
title="SimpleBlog"
|
||||
href="http://example.com/rss.xml" />
|
||||
</head>
|
||||
~~~~~~
|
||||
|
||||
As you remember, in our previous blog, all posts had a file name like
|
||||
`posts/yyyy-mm-dd-title.extension`, as is the Hakyll convention. But they also
|
||||
had a metadata field `date`, containing a human-readable date. This is not very
|
||||
D.R.Y., of course! Hakyll has a specialized `renderValue` function to deal with
|
||||
dates encoded in paths: `renderDate`.
|
||||
This makes most browsers see the rss feed, and show it in the address bar.
|
||||
If you want, you can also add a pretty button to your blog linking to
|
||||
`rss.xml`.
|
||||
|
||||
~~~~~{.haskell}
|
||||
postManipulation :: ContextManipulation
|
||||
postManipulation = renderDate "date" "%B %e, %Y" "Unknown date"
|
||||
~~~~~
|
||||
## That's it!
|
||||
|
||||
That manipulation will:
|
||||
- Read the date from the file name the post was loaded from.
|
||||
- Parse the date and render it in a `%B %e, %Y` format. This is a
|
||||
`Month day, Year` format.
|
||||
- Put the result in the `date` metadata field.
|
||||
- If the date could not be parsed, it will put `"Unknown date"` in the `date`
|
||||
metadata field.
|
||||
|
||||
So, we can throw away our `date: ` lines from our posts, and still use `$date`
|
||||
in our templates.
|
||||
|
||||
## Abstracting the post list
|
||||
|
||||
Now, we're going to render tags. This is also done using context manipulations.
|
||||
Hakyll has a specialized module to deal with tags, provided by
|
||||
`Text.Hakyll.Tags`. This module assumes tags are comma separated, and placed in
|
||||
the `tags` metadata field.
|
||||
|
||||
> ---
|
||||
> title: A third post
|
||||
> author: Publius Ovidius Naso
|
||||
> tags: epic fail, ovidius
|
||||
> ---
|
||||
> Pellentesque tempor blandit elit, vel...
|
||||
|
||||
But first things first. We need to render a post list for every tag. We already
|
||||
had some code to render a list of all posts. We're just going to abstract this
|
||||
code into a more general function:
|
||||
|
||||
~~~~{.haskell}
|
||||
renderPostList url title posts = do
|
||||
let postItems = renderAndConcatWith
|
||||
postManipulation
|
||||
"templates/postitem.html"
|
||||
(map createPagePath posts)
|
||||
customPage = createCustomPage
|
||||
url
|
||||
("templates/postitem.html" : posts)
|
||||
[ ("title", Left title)
|
||||
, ("posts", Right postItems)
|
||||
]
|
||||
renderChain ["posts.html", "templates/default.html"]
|
||||
customPage
|
||||
~~~~~
|
||||
|
||||
Our "render all posts" action can now be written as:
|
||||
|
||||
~~~~~{.haskell}
|
||||
renderPostList "posts.html" "All posts" postPaths
|
||||
~~~~~
|
||||
|
||||
## Tag links
|
||||
|
||||
We want to display the tags for our post under the title. But if we use the
|
||||
`$tags` key in a template, we will just have the plain tags - they will not be
|
||||
clickable. We can again solve this with a `ContextManipulation`. We have a
|
||||
function that produces an url for a given tag:
|
||||
|
||||
~~~~~{.haskell}
|
||||
tagToURL tag = "/tags/" ++ removeSpaces tag ++ ".html"
|
||||
~~~~~
|
||||
|
||||
`removeSpaces` is an auxiliary function from `Text.Hakyll.File`. Now, there is
|
||||
a specialized `renderValue` function for creating linked tags called
|
||||
`renderTagLinks`. This function simply takes a function that produces an url
|
||||
for a given tag - the function we just wrote. Let's extend our
|
||||
`postManipulation`.
|
||||
|
||||
~~~~~{.haskell}
|
||||
postManipulation :: ContextManipulation
|
||||
postManipulation = renderDate "date" "%B %e, %Y" "Unknown date"
|
||||
. renderTagLinks tagToURL
|
||||
~~~~~
|
||||
|
||||
If we click a tag, we get a `404`. That's because we haven't generated the
|
||||
post lists for every tag.
|
||||
|
||||
## The Tag Map
|
||||
|
||||
Hakyll provides a function called `readTagMap`. Let's inspect it's type.
|
||||
|
||||
~~~~~{.haskell}
|
||||
readTagMap [FilePath] -> IO Map String [FilePath]
|
||||
~~~~~
|
||||
|
||||
You give it a list of paths, and it creates a map that, for every tag, holds
|
||||
a number of posts. We can easily use this to render a post list for every tag.
|
||||
|
||||
~~~~~{.haskell}
|
||||
tagMap <- readTagMap postPaths
|
||||
let renderListForTag (tag, posts) =
|
||||
renderPostList (tagToURL tag)
|
||||
("Posts tagged " ++ tag)
|
||||
mapM_ renderListForTag (toList tagMap)
|
||||
~~~~~
|
||||
|
||||
There we go. We now have clickable tags, and a post list for every tag.
|
||||
|
||||
## A Tag Cloud
|
||||
|
||||
A tag cloud is a commonly found thing on blogs. Hakyll also provides code to
|
||||
generate a tag cloud. Let's have a look at the `renderTagCloud` function.
|
||||
|
||||
~~~~~{.haskell}
|
||||
TagCloud :: M.Map String [FilePath]
|
||||
-> (String -> String)
|
||||
-> Float
|
||||
-> Float
|
||||
-> String
|
||||
~~~~~
|
||||
|
||||
The first argument is obviously the result of the `readTagMap` function. The
|
||||
second argument is, once again, a function to create an url for a given tag.
|
||||
Then, we give a minimum and a maximum font size in percent, and we get the
|
||||
tag cloud back. We can add this to our index:
|
||||
|
||||
~~~~~{.haskell}
|
||||
let tagCloud = renderTagCloud tagMap tagToURL 100 200
|
||||
...
|
||||
createCustomPage "index.html"
|
||||
("templates/postitem.html" : take 3 postPaths)
|
||||
[ ("title", Left "Home")
|
||||
, ("posts", Right recentPosts)
|
||||
, ("tagcloud", Left tagCloud)
|
||||
]
|
||||
~~~~~
|
||||
|
||||
## That's it
|
||||
|
||||
Feel free to hack around with the code from the zip file. As always, if you
|
||||
still have questions, ask them at the
|
||||
Yep, that's it. Feel free to play with the source code in
|
||||
[the zip file](examples/rssblog.zip) and extend the blog further. As always,
|
||||
all questions are welcome on the
|
||||
[google discussion group](http://groups.google.com/group/hakyll).
|
||||
|
|
211
examples/hakyll/tutorial5.markdown
Normal file
211
examples/hakyll/tutorial5.markdown
Normal file
|
@ -0,0 +1,211 @@
|
|||
---
|
||||
title: Tutorial (Part V)
|
||||
what: adds tags and context manipulations to our blog
|
||||
---
|
||||
|
||||
Here, have [a zip file](examples/tagblog.zip) for this tutorial.
|
||||
|
||||
## Context manipulations
|
||||
|
||||
As you might remember, `Renderable` objects are usually just key-value mappings.
|
||||
We can render those with templates, and then the `$key`'s in the template get
|
||||
substituted by the appropriate values. This is a rather flexible system, but
|
||||
there are limitations. Some of these limitations can be solved using
|
||||
_Context Manipulations_.
|
||||
|
||||
In `Text.Hakyll.Context`, we see the type of `ContextManipulation`:
|
||||
|
||||
~~~~~{.haskell}
|
||||
type ContextManipulation = Context -> Context
|
||||
~~~~~
|
||||
|
||||
Where `Context` is a key-value mapping. Usually, you don't create
|
||||
`ContextManipulation`'s by hand, but you use the `renderValue` function. Let's
|
||||
have a look at it's type.
|
||||
|
||||
~~~~~{.haskell}
|
||||
renderValue :: String -> String
|
||||
-> (String -> String)
|
||||
-> ContextManipulation
|
||||
~~~~~
|
||||
|
||||
This is the preferred way of creating context manipulations. The first argument
|
||||
is the `key` to manipulate. The second argument is the `key` where the new value
|
||||
should be placed. If this is the same as the first argument, it will be
|
||||
replaced. The third argument is the function to manipulate the `value` with.
|
||||
|
||||
As a simple example, let's write a function that puts the `$title` in uppercase.
|
||||
|
||||
~~~~~{.haskell}
|
||||
import Data.Char (toUpper)
|
||||
|
||||
titleUpper :: ContextManipulation
|
||||
titleUpper = renderValue "title" "title" $ map toUpper
|
||||
~~~~~
|
||||
|
||||
## Applying Context Manipulations
|
||||
|
||||
Now, the question is how to apply these `ContextManipulation`'s. The answer is
|
||||
simple. For every important render function (`render`, `renderChain`,
|
||||
`renderAndConcat`), there is a variant that takes a `ContextManipulation` as a
|
||||
first argument. These functions are thus: `renderWith`, `renderChainWith`,
|
||||
`renderAndConcatWith`. In fact, the following holds true:
|
||||
|
||||
~~~~~{.haskell}
|
||||
render == renderWith id
|
||||
renderChain == renderChainWith id
|
||||
renderAndConcat == renderAndConcatWith id
|
||||
~~~~~
|
||||
|
||||
So we could use or title manipulation like this:
|
||||
|
||||
~~~~~{.haskell}
|
||||
renderChainWith titleUpper ["templates/default.html"]
|
||||
(createPagePath "index.markdown")
|
||||
~~~~~
|
||||
|
||||
## Rendering dates
|
||||
|
||||
As you remember, in our previous blog, all posts had a file name like
|
||||
`posts/yyyy-mm-dd-title.extension`, as is the Hakyll convention. But they also
|
||||
had a metadata field `date`, containing a human-readable date. This is not very
|
||||
D.R.Y., of course! Hakyll has a specialized `renderValue` function to deal with
|
||||
dates encoded in paths: `renderDate`.
|
||||
|
||||
~~~~~{.haskell}
|
||||
postManipulation :: ContextManipulation
|
||||
postManipulation = renderDate "date" "%B %e, %Y" "Unknown date"
|
||||
~~~~~
|
||||
|
||||
That manipulation will:
|
||||
- Read the date from the file name the post was loaded from.
|
||||
- Parse the date and render it in a `%B %e, %Y` format. This is a
|
||||
`Month day, Year` format.
|
||||
- Put the result in the `date` metadata field.
|
||||
- If the date could not be parsed, it will put `"Unknown date"` in the `date`
|
||||
metadata field.
|
||||
|
||||
So, we can throw away our `date: ` lines from our posts, and still use `$date`
|
||||
in our templates.
|
||||
|
||||
## Abstracting the post list
|
||||
|
||||
Now, we're going to render tags. This is also done using context manipulations.
|
||||
Hakyll has a specialized module to deal with tags, provided by
|
||||
`Text.Hakyll.Tags`. This module assumes tags are comma separated, and placed in
|
||||
the `tags` metadata field.
|
||||
|
||||
---
|
||||
title: A third post
|
||||
author: Publius Ovidius Naso
|
||||
tags: epic fail, ovidius
|
||||
---
|
||||
Pellentesque tempor blandit elit, vel...
|
||||
|
||||
But first things first. We need to render a post list for every tag. We already
|
||||
had some code to render a list of all posts. We're just going to abstract this
|
||||
code into a more general function:
|
||||
|
||||
~~~~{.haskell}
|
||||
renderPostList url title posts = do
|
||||
let postItems = renderAndConcatWith
|
||||
postManipulation
|
||||
"templates/postitem.html"
|
||||
(map createPagePath posts)
|
||||
customPage = createCustomPage
|
||||
url
|
||||
("templates/postitem.html" : posts)
|
||||
[ ("title", Left title)
|
||||
, ("posts", Right postItems)
|
||||
]
|
||||
renderChain ["posts.html", "templates/default.html"]
|
||||
customPage
|
||||
~~~~~
|
||||
|
||||
Our "render all posts" action can now be written as:
|
||||
|
||||
~~~~~{.haskell}
|
||||
renderPostList "posts.html" "All posts" postPaths
|
||||
~~~~~
|
||||
|
||||
## Tag links
|
||||
|
||||
We want to display the tags for our post under the title. But if we use the
|
||||
`$tags` key in a template, we will just have the plain tags - they will not be
|
||||
clickable. We can again solve this with a `ContextManipulation`. We have a
|
||||
function that produces an url for a given tag:
|
||||
|
||||
~~~~~{.haskell}
|
||||
tagToURL tag = "/tags/" ++ removeSpaces tag ++ ".html"
|
||||
~~~~~
|
||||
|
||||
`removeSpaces` is an auxiliary function from `Text.Hakyll.File`. Now, there is
|
||||
a specialized `renderValue` function for creating linked tags called
|
||||
`renderTagLinks`. This function simply takes a function that produces an url
|
||||
for a given tag - the function we just wrote. Let's extend our
|
||||
`postManipulation`.
|
||||
|
||||
~~~~~{.haskell}
|
||||
postManipulation :: ContextManipulation
|
||||
postManipulation = renderDate "date" "%B %e, %Y" "Unknown date"
|
||||
. renderTagLinks tagToURL
|
||||
~~~~~
|
||||
|
||||
If we click a tag, we get a `404`. That's because we haven't generated the
|
||||
post lists for every tag.
|
||||
|
||||
## The Tag Map
|
||||
|
||||
Hakyll provides a function called `readTagMap`. Let's inspect it's type.
|
||||
|
||||
~~~~~{.haskell}
|
||||
readTagMap [FilePath] -> IO Map String [FilePath]
|
||||
~~~~~
|
||||
|
||||
You give it a list of paths, and it creates a map that, for every tag, holds
|
||||
a number of posts. We can easily use this to render a post list for every tag.
|
||||
|
||||
~~~~~{.haskell}
|
||||
tagMap <- readTagMap postPaths
|
||||
let renderListForTag (tag, posts) =
|
||||
renderPostList (tagToURL tag)
|
||||
("Posts tagged " ++ tag)
|
||||
mapM_ renderListForTag (toList tagMap)
|
||||
~~~~~
|
||||
|
||||
There we go. We now have clickable tags, and a post list for every tag.
|
||||
|
||||
## A Tag Cloud
|
||||
|
||||
A tag cloud is a commonly found thing on blogs. Hakyll also provides code to
|
||||
generate a tag cloud. Let's have a look at the `renderTagCloud` function.
|
||||
|
||||
~~~~~{.haskell}
|
||||
TagCloud :: M.Map String [FilePath]
|
||||
-> (String -> String)
|
||||
-> Float
|
||||
-> Float
|
||||
-> String
|
||||
~~~~~
|
||||
|
||||
The first argument is obviously the result of the `readTagMap` function. The
|
||||
second argument is, once again, a function to create an url for a given tag.
|
||||
Then, we give a minimum and a maximum font size in percent, and we get the
|
||||
tag cloud back. We can add this to our index:
|
||||
|
||||
~~~~~{.haskell}
|
||||
let tagCloud = renderTagCloud tagMap tagToURL 100 200
|
||||
...
|
||||
createCustomPage "index.html"
|
||||
("templates/postitem.html" : take 3 postPaths)
|
||||
[ ("title", Left "Home")
|
||||
, ("posts", Right recentPosts)
|
||||
, ("tagcloud", Left tagCloud)
|
||||
]
|
||||
~~~~~
|
||||
|
||||
## That's it
|
||||
|
||||
Feel free to hack around with the code from the zip file. As always, if you
|
||||
still have questions, ask them at the
|
||||
[google discussion group](http://groups.google.com/group/hakyll).
|
27
examples/morepages/about.markdown
Normal file
27
examples/morepages/about.markdown
Normal file
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
title: About
|
||||
|
||||
--- section1
|
||||
## Mattis
|
||||
Nullam imperdiet sodales orci vitae molestie. Nunc quam orci, pharetra a
|
||||
rhoncus vitae, eleifend id felis. Suspendisse potenti. Etiam vitae urna orci.
|
||||
Quisque pellentesque dignissim felis, egestas tempus urna luctus vitae. In hac
|
||||
habitasse platea dictumst. Morbi fringilla mattis odio, et mattis tellus
|
||||
accumsan vitae.
|
||||
|
||||
--- section2
|
||||
## Orci
|
||||
Vivamus eget mauris sit amet nulla laoreet lobortis. Nulla in diam elementum
|
||||
risus convallis commodo. Cras vehicula varius dui vitae facilisis. Proin
|
||||
elementum libero eget leo aliquet quis euismod orci vestibulum. Duis rhoncus
|
||||
lorem consequat tellus vestibulum aliquam. Quisque orci orci, malesuada porta
|
||||
blandit et, interdum nec magna.
|
||||
|
||||
--- section3
|
||||
## Augue
|
||||
In urna ante, pulvinar et imperdiet nec, fermentum ac tortor. Cras tristique
|
||||
pellentesque euismod. Pellentesque est ante, sagittis vitae vehicula vitae,
|
||||
ullamcorper eget lectus. Curabitur egestas accumsan leo, ac ullamcorper nibh
|
||||
tincidunt id. Curabitur lorem libero, fermentum non tincidunt ac, pretium in
|
||||
libero. Donec vel mi eu tortor accumsan dictum ut in augue. Vestibulum est
|
||||
lorem, bibendum eu vehicula eu, convallis eget mauris.
|
27
examples/morepages/css/default.css
Normal file
27
examples/morepages/css/default.css
Normal file
|
@ -0,0 +1,27 @@
|
|||
body {
|
||||
width: 600px;
|
||||
margin: 0px auto 0px auto;
|
||||
}
|
||||
|
||||
div#navigation {
|
||||
text-align: center;
|
||||
border-bottom: 4px solid black;
|
||||
}
|
||||
|
||||
div#navigation a {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
background-color: black;
|
||||
padding: 3px 10px 3px 10px;
|
||||
margin: 0px 10px 0px 10px;
|
||||
}
|
||||
|
||||
div.column {
|
||||
width: 30%;
|
||||
float: left;
|
||||
margin: 0px 10px 0px 10px;
|
||||
}
|
||||
|
||||
div#footer {
|
||||
clear: both;
|
||||
}
|
16
examples/morepages/footer.markdown
Normal file
16
examples/morepages/footer.markdown
Normal file
|
@ -0,0 +1,16 @@
|
|||
--- footer
|
||||
## Sapien
|
||||
|
||||
In hac habitasse platea dictumst. Cras placerat felis nec risus varius in
|
||||
accumsan sem fermentum. Vestibulum elementum aliquam tortor semper vulputate.
|
||||
Vivamus tincidunt tellus sed purus tempor fringilla. Morbi dui nisl, eleifend
|
||||
non dictum vitae, luctus eu lacus. Duis vitae lacus sem, ut porta mauris.
|
||||
Aenean sed ultricies dui. Vivamus ullamcorper metus lorem, at ornare nibh.
|
||||
Mauris mi metus, convallis id lobortis vitae, interdum quis felis. Cras elit
|
||||
massa, pellentesque sit amet pharetra ut, volutpat in arcu. Vivamus blandit,
|
||||
ligula et ultricies consequat, metus sem congue quam, ac pretium enim velit at
|
||||
tortor. Cras in tellus eu sapien pulvinar sollicitudin eu id ipsum. Mauris nec
|
||||
urna tellus, et scelerisque tellus. Nunc imperdiet felis nec libero consectetur
|
||||
tristique tristique ipsum sodales. Cras tortor nisl, condimentum in
|
||||
pellentesque id, interdum vel mi. Suspendisse auctor vehicula orci at
|
||||
scelerisque. Vivamus quis sagittis felis.
|
13
examples/morepages/hakyll.hs
Normal file
13
examples/morepages/hakyll.hs
Normal file
|
@ -0,0 +1,13 @@
|
|||
import Text.Hakyll (hakyll, defaultHakyllConfiguration)
|
||||
import Text.Hakyll.File (directory)
|
||||
import Text.Hakyll.Render (css, static, renderChain)
|
||||
import Text.Hakyll.Renderables (createPagePath, combine)
|
||||
|
||||
main = hakyll defaultHakyllConfiguration $ do
|
||||
directory css "css"
|
||||
render "about.markdown"
|
||||
render "index.markdown"
|
||||
render "products.markdown"
|
||||
where
|
||||
render = renderChain ["templates/default.html"] . withFooter . createPagePath
|
||||
withFooter a = a `combine` createPagePath "footer.markdown"
|
24
examples/morepages/index.markdown
Normal file
24
examples/morepages/index.markdown
Normal file
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
title: Home
|
||||
|
||||
--- section1
|
||||
## Purus
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce tempor, urna et
|
||||
auctor tincidunt, eros mauris facilisis purus, eget sollicitudin leo massa sit
|
||||
amet ipsum. Vivamus eu massa in urna vehicula rutrum eget sit amet purus.
|
||||
|
||||
--- section2
|
||||
## Ligula
|
||||
Mauris sit amet justo mi. Curabitur vel quam felis. In hac habitasse platea
|
||||
dictumst. Etiam nec consequat risus. Donec consequat est vitae neque fermentum
|
||||
feugiat nec ac nibh. Nulla rhoncus, odio quis scelerisque rutrum, metus sem
|
||||
tempor ante, a ornare ipsum felis sed ligula. Morbi urna lectus, scelerisque
|
||||
non pharetra in, rutrum quis ligula. Phasellus semper ullamcorper arcu eu
|
||||
auctor.
|
||||
|
||||
--- section3
|
||||
## Justo
|
||||
Aliquam sagittis tincidunt libero ut elementum. Ut sit amet vestibulum metus.
|
||||
Ut aliquet congue neque eu tincidunt. Integer eu elit sed massa sollicitudin
|
||||
vehicula nec ut sem. Cras euismod enim eget purus lacinia non feugiat urna
|
||||
imperdiet. Aliquam justo sem, viverra eu vehicula vitae, imperdiet vel magna.
|
23
examples/morepages/products.markdown
Normal file
23
examples/morepages/products.markdown
Normal file
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
title: Products
|
||||
|
||||
--- section1
|
||||
## Lacus
|
||||
Etiam condimentum auctor semper. Donec lobortis, magna id sodales sollicitudin,
|
||||
lectus mi egestas nulla, pulvinar lobortis nunc eros id nisl. Curabitur
|
||||
imperdiet, erat at accumsan vulputate, purus nunc blandit nulla, dictum
|
||||
vestibulum sem lorem eget ipsum.
|
||||
|
||||
--- section2
|
||||
## Vitae
|
||||
Integer ut dui eu felis mollis vestibulum. Etiam at nibh id diam aliquet
|
||||
vestibulum sit amet a nibh. Aliquam erat volutpat. Etiam vitae nulla at dolor
|
||||
fringilla tempor ut a nunc. Pellentesque elementum elit lorem. Quisque nec
|
||||
ligula ipsum. Nunc augue lacus, ullamcorper vel dapibus in, mattis eget elit.
|
||||
|
||||
--- section3
|
||||
## Feugiat
|
||||
Pellentesque enim dui, interdum elementum vehicula luctus, feugiat vitae arcu.
|
||||
Vestibulum ut felis justo. Quisque vestibulum mauris eget ipsum luctus
|
||||
consequat. Nunc tincidunt, turpis ut fermentum dapibus,
|
||||
justo tortor bibendum sem, at facilisis justo odio luctus lectus.
|
22
examples/morepages/templates/default.html
Normal file
22
examples/morepages/templates/default.html
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<title>MyAweSomeCompany - $title</title>
|
||||
<link rel="stylesheet" type="text/css" href="$root/css/default.css" />
|
||||
</head>
|
||||
<body>
|
||||
<h1>MyAweSomeCompany - $title</h1>
|
||||
<div id="navigation">
|
||||
<a href="$root/index.html">Home</a>
|
||||
<a href="$root/about.html">About</a>
|
||||
<a href="$root/products.html">Products</a>
|
||||
</div>
|
||||
|
||||
<div class="column"> $section1 </div>
|
||||
<div class="column"> $section2 </div>
|
||||
<div class="column"> $section3 </div>
|
||||
<div id="footer"> $footer </div>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in a new issue