>" /> >" /> >"/>

Custom content on a tags page

76 views Asked by At

I have setup Hakyll to generate basic tag pages from blog posts as follows:

main = do    
    hakyll $ do

        match "preambles/*.md" $ compile $ pandocCompiler >>= relativizeUrls

        tags <- buildTags "posts/*.md" (fromCapture "tags/*.html")

        tagsRules tags $ \tag pattern -> do
            let title = "Posts tagged \"" ++ tag ++ "\""
            route idRoute
            compile $ do
                posts <- recentFirst =<< loadAll pattern
                preamble <- loadBody $ fromFilePath ("preambles/" ++ tag ++ ".html")
                let ctx = constField "title" title
                        `mappend` listField "posts" postCtx (return posts)
                        `mappend` bodyField preamble
                        `mappend` defaultContext

                makeItem ""
                    >>= loadAndApplyTemplate "templates/tag.html" ctx
                    >>= loadAndApplyTemplate "templates/default.html" ctx
                    >>= relativizeUrls
...

I would like to be able to supply an optional preamble for each page.

To do this I would expect to have a markdown file per tag in a preambles directory, and attempt to use these when building the tags pages. However, I can't figure out how to make this optional, because loadSnapshot will fail if not found.

Is this an appropriate approach, and if so, how would I handle missing data?

Also, I'm not sure that bodyField is the appropriate way to pass the data to the template.

1

There are 1 answers

1
Joe On

This seems like an entirely reasonable approach to me.

To answer your main question, you can take advantage of the Alternative instance on Compiler to provide a fallback.

I'd like to suggest a few minor changes too.


First off, replace your preamble rule with:

let preamblePattern = "preambles/*.md"
match preamblePattern $ do
    compile $ pandocCompiler 
                 >>= relativizeUrls
                 >>= saveSnapshot "preamble"
                 >>= pure . void

>>= pure . void converts the Compiler (Item String) into a Compiler (Item ()). This prevents Hakyll from producing any output files for this rule. The saveSnapshot "preamble" means we can still get at the content later.

In addition to this, I've extracted the pattern into a variable, for reasons that will become clear later.


Then, replace:

preamble <- loadBody $ fromFilePath ("preambles/" ++ tag ++ ".html")

with:

let preambleIdent = fromCapture preamblePattern tag
preamble <- (loadSnapshotBody preambleIdent "preamble") <|> pure "fallback"

This uses fromCapture, to generate the identifier from the Pattern. This reduces duplication, and also sidesteps the bug in your code where you used the extension ".html" when the Identifier is generated from the original filepath, and has extension ".md".

This uses loadSnapshotBody rather than loadBody because of the change suggested above, where we remove the content from the rule output.

Most importantly, it uses <|> from Alternative to provide a fallback. You'll probably want to replace "fallback" with whichever default value you want.


Finally, makeItem "" needs to be replaced with makeItem preamble.