Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
317 lines (270 sloc) 13.8 KB
--------------------------------------------------------------------------------
{-# LANGUAGE OverloadedStrings #-}
import Control.Monad ((>=>), forM_)
import Data.Maybe (fromMaybe)
import Data.Monoid ((<>))
import Hakyll
import Hakyll.Images ( loadImage
, compressJpgCompiler
, scaleImageCompiler )
-- Hakyll can trip on characters like apostrophes
-- https://github.com/jaspervdj/hakyll/issues/109
import qualified GHC.IO.Encoding as E
import Text.Pandoc.Definition (Pandoc(..), Meta(..), Inline(..), MetaValue(..))
import Text.Pandoc.Extensions
import Text.Pandoc.Filter.Pyplot (plotTransformWithConfig, configuration)
import qualified Text.Pandoc.Filter.Pyplot as P
import Text.Pandoc.Highlighting
import Text.Pandoc.Options
import Text.Pandoc.Walk (walkM)
import System.IO
import qualified Data.ByteString as B
import Data.Map (foldMapWithKey)
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import Text.Blaze.Html.Renderer.String (renderHtml)
import qualified Text.Blaze.Html.Renderer.Pretty as P
import Text.Blaze.Html.Renderer.Utf8 (renderHtmlToByteStringIO)
import BulmaFilter (bulmaTransform)
import BulmaTemplate (mkDefaultTemplate,
tocTemplate)
import Data.Time.Calendar (showGregorian)
import Data.Time.Clock (getCurrentTime, utctDay)
import Feed (feedConfiguration)
-- | syntax highlighting style to use throughout
syntaxHighlightingStyle :: Style
syntaxHighlightingStyle = kate
-- We match images down to two levels
-- Images/* and images/*/**
jpgImages = "images/*.jpg" .||. "images/*/**.jpg"
nonJpgImages = ( "images/*/**"
.||. "images/*"
) .&&. complement jpgImages
generatedContent = "generated/**"
--------------------------------------------------------------------------------
-- | Site configuration
-- The remote folder is WWW/decotret. The destination folder
-- (where the website is rendered) must also be called "decotret/"
-- because of the way scp works.
conf :: Configuration
conf = defaultConfiguration
{ destinationDirectory = "decotret"
, deployCommand = "scp -Cr ./decotret decotret@gollum.physics.mcgill.ca:/common/WWW/"
}
renderTemplate :: IO B.ByteString
renderTemplate = do
today <- getCurrentTime >>= return . showGregorian . utctDay
let template = mkDefaultTemplate (mconcat ["Page generated on ", today, ". "])
return template
-- We need to go through Text because of utf8-encoding
>>= return . T.encodeUtf8 . T.pack . P.renderHtml
--------------------------------------------------------------------------------
main :: IO ()
main = do
-- Hakyll can trip on characters like apostrophes
-- https://github.com/jaspervdj/hakyll/issues/109
E.setLocaleEncoding E.utf8
pyplotConfig <- configuration ".pandoc-pyplot.yml"
hakyllWith conf $ do
preprocess $ do
-- generate the CSS required to to syntax highlighting
let css = styleToCss syntaxHighlightingStyle
writeFile "css/syntax.css" css >> putStrLn " Generated css\\syntax.css"
-- We generate the default template
-- The template has a marking showing on what date was the page generated
renderTemplate
>>= (B.writeFile "templates/default.html")
>> putStrLn " Generated templates\\default.html"
--------------------------------------------------------------------------------
-- A lot of things can be compied directly
forM_ ["files/*", "fonts/*", "js/*", nonJpgImages] $
\pattern ->
match pattern $ do
route idRoute
compile copyFileCompiler
-- JPG images are special: they can be compressed
match jpgImages $ do
route idRoute
compile $ loadImage
>>= compressJpgCompiler 50
match generatedContent $ do
route generatedRoute
compile copyFileCompiler
match "css/*" $ do
route idRoute
compile compressCssCompiler
--------------------------------------------------------------------------------
-- These are static pages, like the "about" page
-- Note that /static/index.html is a special case and is handled below
match "static/*.md" $ do
route $ (setExtension "html") `composeRoutes` staticRoute
compile $ pandocCompiler_ pyplotConfig
>>= loadAndApplyTemplate "templates/default.html" defaultContext
>>= relativizeUrls
--------------------------------------------------------------------------------
-- Compile projects page
-- We need to compile each project individually first
-- If this is not done, we cannot use the metadata in HTML templates
match ("projects/**.md") $ compile $ pandocCompiler_ pyplotConfig >>= relativizeUrls
create ["software.html"] $ do
route idRoute
compile $ do
scientific <- loadAll (fromGlob "projects/scientific/*.md")
general <- loadAll (fromGlob "projects/*.md")
let projectsCtx = mconcat [
listField "scientific" defaultContext (return scientific)
, listField "general" defaultContext (return general)
, constField "title" "Software projects"
, defaultContext
]
makeItem ""
>>= loadAndApplyTemplate "templates/projects.html" projectsCtx
>>= loadAndApplyTemplate "templates/default.html" projectsCtx
>>= relativizeUrls
--------------------------------------------------------------------------------
-- The images used for the pandoc-pyplot examples
-- The images must be in the root directory
-- for the rendered website
match ("images/pandoc-pyplot-gallery/*") $ do
route (gsubRoute "images/pandoc-pyplot-gallery/" (const ""))
compile copyFileCompiler
--------------------------------------------------------------------------------
-- Compile blog posts
-- Explicitly do not match the drafts
--
-- TODO: include Pandoc metainformation to implement reading-time filter
-- See for example here:
-- https://github.com/jaspervdj/hakyll/issues/643
match ("posts/*" .&&. complement "posts/drafts/*") $ do
route $ setExtension "html"
compile $ pandocCompiler_ pyplotConfig
-- Post template is obsolete
-- It is now built in the default template
-- >>= loadAndApplyTemplate "templates/post.html" postCtx
>>= saveSnapshot "content" -- Saved content for RSS feed
>>= loadAndApplyTemplate "templates/default.html" postCtx
>>= relativizeUrls
--------------------------------------------------------------------------------
-- Create RSS feed and Atom feeds
-- See https://jaspervdj.be/hakyll/tutorials/05-snapshots-feeds.html
forM_ [ ("feed.xml", renderRss)
, ("atom.xml", renderAtom)
] $
\(name, renderFunc) -> create [name] $ do
route idRoute
compile $ do
let feedCtx = postCtx <> bodyField "description"
posts <- fmap (take 10) . recentFirst =<<
loadAllSnapshots "posts/*" "content"
renderFunc feedConfiguration feedCtx posts
--------------------------------------------------------------------------------
-- Create a page containing all posts
create ["archive.html"] $ do
route idRoute
compile $ do
posts <- recentFirst =<< loadAll "posts/*"
let archiveCtx =
listField "posts" postCtx (return posts) <>
constField "title" "Blog posts" <>
defaultContext
makeItem ""
>>= loadAndApplyTemplate "templates/archive.html" archiveCtx
>>= loadAndApplyTemplate "templates/default.html" archiveCtx
>>= relativizeUrls
--------------------------------------------------------------------------------
-- Generate the home page, including recent blog posts
match "static/index.html" $ do
route staticRoute
compile $ do
posts <- recentFirst =<< loadAll "posts/*"
let indexCtx =
listField "posts" postCtx (return posts) <>
constField "title" "Welcome to my homepage" <>
defaultContext
getResourceBody
>>= applyAsTemplate indexCtx
>>= loadAndApplyTemplate "templates/default.html" indexCtx
>>= relativizeUrls
--------------------------------------------------------------------------------
-- Create a sitemap for easier search engine integration
-- Courtesy of Robert Pearce <https://robertwpearce.com/hakyll-pt-2-generating-a-sitemap-xml-file.html>
create ["sitemap.xml"] $ do
route idRoute
compile $ do
-- Gather all announcements
posts <- recentFirst =<< loadAll "posts/*"
-- Gather all other pages
pages <- loadAll (fromGlob "static/**")
let allPages = pages <> posts
sitemapCtx =
constField "root" "http://www.physics.mcgill.ca/~decotret" <>
listField "pages" postCtx (return allPages)
makeItem ""
>>= loadAndApplyTemplate "templates/sitemap.xml" sitemapCtx
--------------------------------------------------------------------------------
match "templates/*" $ compile templateCompiler
--------------------------------------------------------------------------------
postCtx :: Context String
postCtx = mconcat [ defaultContext
, constField "root" "http://www.physics.mcgill.ca/~decotret/"
, dateField "date" "%Y-%m-%d"
]
-- | Allow math display, code highlighting, table-of-content, and Pandoc filters
-- Note that the Bulma pandoc filter is always applied last
pandocCompiler_ :: P.Configuration -- ^ Pandoc-pyplot configuration
-> Compiler (Item String)
pandocCompiler_ config = do
ident <- getUnderlying
toc <- getMetadataField ident "withtoc"
tocDepth <- getMetadataField ident "tocdepth"
let extensions = defaultPandocExtensions
writerOptions = case toc of
Just _ -> defaultHakyllWriterOptions
{ writerExtensions = extensions
, writerHTMLMathMethod = MathJax ""
, writerHighlightStyle = Just syntaxHighlightingStyle
, writerTableOfContents = True
, writerTOCDepth = read (fromMaybe "3" tocDepth) :: Int
, writerTemplate = Just $ renderHtml tocTemplate
}
Nothing -> defaultHakyllWriterOptions
{ writerExtensions = extensions
, writerHTMLMathMethod = MathJax ""
, writerHighlightStyle = Just syntaxHighlightingStyle
}
pandocCompilerWithTransformM
defaultHakyllReaderOptions
writerOptions
(unsafeCompiler . transforms)
where
transforms doc = bulmaTransform <$> plotTransformWithConfig config doc
-- Pandoc extensions used by the compiler
defaultPandocExtensions :: Extensions
defaultPandocExtensions =
let extensions = [
-- Pandoc Extensions: http://pandoc.org/MANUAL.html#extensions
-- Math extensions
Ext_tex_math_dollars
, Ext_tex_math_double_backslash
, Ext_latex_macros
-- Code extensions
, Ext_fenced_code_blocks
, Ext_backtick_code_blocks
, Ext_fenced_code_attributes
, Ext_inline_code_attributes -- Inline code attributes (e.g. `<$>`{.haskell})
-- Markdown extensions
, Ext_implicit_header_references -- We also allow implicit header references (instead of inserting <a> tags)
, Ext_definition_lists -- Definition lists based on PHP Markdown
, Ext_yaml_metadata_block -- Allow metadata to be speficied by YAML syntax
, Ext_superscript -- Superscripts (2^10^ is 1024)
, Ext_subscript -- Subscripts (H~2~O is water)
, Ext_footnotes -- Footnotes ([^1]: Here is a footnote)
]
defaultExtensions = writerExtensions defaultHakyllWriterOptions
in foldr enableExtension defaultExtensions extensions
-- Move content from static/ folder to base folder
staticRoute :: Routes
staticRoute = (gsubRoute "static/" (const ""))
-- Move generated posts from posts/generated to generated/
generatedRoute :: Routes
generatedRoute = gsubRoute "generated/" (const "posts/generated/")
You can’t perform that action at this time.