summaryrefslogtreecommitdiff
path: root/src/Hakyll/Web/Page.hs
blob: 6c219b40cae916119355a08a1e005fa94cb7a827 (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
-- | A page is a key-value mapping, representing a page on your site
--
-- A page is an important concept in Hakyll. It is a key-value mapping, and has
-- one field with an arbitrary type. A 'Page' thus consists of
--
-- * metadata (of the type @Map String String@);
--
-- * the actual value (of the type @a@).
--
-- Usually, the value will be a 'String' as well, and the value will be the body
-- of the page.
--
-- However, this is certainly no restriction. For example, @Page ByteString@
-- could be used to represent a binary item (e.g. an image) and some metadata.
--
-- Pages can be constructed using Haskell, but they are usually parsed from a
-- file. The file format for pages is pretty straightforward.
--
-- > This is a simple page
-- > consisting of two lines.
--
-- This is a valid page with two lines. If we load this in Hakyll, there would
-- be no metadata, and the body would be the given text. Let's look at a page
-- with some metadata:
--
-- > ---
-- > title: Alice's Adventures in Wonderland
-- > author: Lewis Caroll
-- > year: 1865
-- > ---
-- >
-- > Chapter I
-- > =========
-- >
-- > Down the Rabbit-Hole
-- > --------------------
-- >
-- > Alice was beginning to get very tired of sitting by her sister on the bank,
-- > and of having nothing to do: once or twice she had peeped into the book her
-- > sister was reading, but it had no pictures or conversations in it, "and
-- > what is the use of a book," thought Alice "without pictures or
-- > conversation?"
-- >
-- > ...
--
-- As you can see, we construct a metadata header in Hakyll using @---@. Then,
-- we simply list all @key: value@ pairs, and end with @---@ again. This page
-- contains three metadata fields and a body. The body is given in markdown
-- format, which can be easily rendered to HTML by Hakyll, using pandoc.
--
{-# LANGUAGE DeriveDataTypeable #-}
module Hakyll.Web.Page
    ( Page (..)
    , fromBody
    , fromMap
    , toMap
    , readPageCompiler
    , pageCompiler
    , pageCompilerWith
    , pageCompilerWithPandoc
    , pageCompilerWithFields
    , addDefaultFields
    ) where

import Prelude hiding (id)
import Control.Category (id)
import Control.Arrow (arr, (>>^), (&&&), (>>>))
import System.FilePath (takeBaseName, takeDirectory)
import qualified Data.Map as M

import Text.Pandoc (Pandoc, ParserState, WriterOptions)

import Hakyll.Core.Identifier
import Hakyll.Core.Compiler
import Hakyll.Core.Resource
import Hakyll.Web.Page.Internal
import Hakyll.Web.Page.Read
import Hakyll.Web.Page.Metadata
import Hakyll.Web.Pandoc
import Hakyll.Web.Template
import Hakyll.Web.Util.Url

-- | Create a page from a body, without metadata
--
fromBody :: a -> Page a
fromBody = Page M.empty

-- | Read a page (do not render it)
--
readPageCompiler :: Compiler Resource (Page String)
readPageCompiler = getResourceString >>^ readPage

-- | Read a page, add default fields, substitute fields and render using pandoc
--
pageCompiler :: Compiler Resource (Page String)
pageCompiler =
    pageCompilerWith defaultHakyllParserState defaultHakyllWriterOptions

-- | A version of 'pageCompiler' which allows you to specify your own pandoc
-- options
--
pageCompilerWith :: ParserState -> WriterOptions
                 -> Compiler Resource (Page String)
pageCompilerWith state options = pageCompilerWithPandoc state options id

-- | An extension of 'pageCompilerWith' which allows you to specify a custom
-- pandoc transformer for the content
--
pageCompilerWithPandoc :: ParserState -> WriterOptions
                       -> (Pandoc -> Pandoc)
                       -> Compiler Resource (Page String)
pageCompilerWithPandoc state options f = cached cacheName $
    readPageCompiler >>> addDefaultFields >>> arr applySelf
                     >>> pageReadPandocWith state
                     >>> arr (fmap (writePandocWith options . f))
  where
    cacheName = "Hakyll.Web.Page.pageCompilerWithPandoc"

-- | This is another, even more advanced version of 'pageCompilerWithPandoc'.
-- This function allows you to provide an arrow which is applied before the
-- fields in a page are rendered. This means you can use this extra customizable
-- stage to add custom fields which are inserted in the page.
--
pageCompilerWithFields :: ParserState -> WriterOptions
                       -> (Pandoc -> Pandoc)
                       -> Compiler (Page String) (Page String)
                       -> Compiler Resource (Page String)
pageCompilerWithFields state options f g =
    readPageCompiler >>> addDefaultFields >>> g >>> arr applySelf
                     >>> pageReadPandocWith state
                     >>> arr (fmap (writePandocWith options . f))

-- | Add a number of default metadata fields to a page. These fields include:
--
-- * @$url$@
--
-- * @$category$@
--
-- * @$title$@
--
-- * @$path$@
--
addDefaultFields :: Compiler (Page a) (Page a)
addDefaultFields =   (getRoute &&& id >>^ uncurry addRoute)
                 >>> (getIdentifier &&& id >>^ uncurry addIdentifier)
  where
    -- Add root and url, based on route
    addRoute Nothing  = id
    addRoute (Just r) = trySetField "url" (toUrl r)

    -- Add title and category, based on identifier
    addIdentifier i = trySetField "title" (takeBaseName p)
                    . trySetField "category" (takeBaseName $ takeDirectory p)
                    . trySetField "path" p
      where
        p = toFilePath i