site: initial commit
This commit is contained in:
parent
23e70fb123
commit
639f8749fd
198
site.hs
Normal file
198
site.hs
Normal file
|
@ -0,0 +1,198 @@
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
import Data.Monoid (mappend)
|
||||||
|
import Data.List (sortBy)
|
||||||
|
import Data.Ord (comparing)
|
||||||
|
import Hakyll
|
||||||
|
import Control.Monad (liftM, forM_)
|
||||||
|
import System.FilePath (takeBaseName)
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
main :: IO ()
|
||||||
|
main = hakyll $ do
|
||||||
|
match ("images/*" .||. "js/*") $ do
|
||||||
|
route idRoute
|
||||||
|
compile copyFileCompiler
|
||||||
|
|
||||||
|
match "css/*" $ do
|
||||||
|
route idRoute
|
||||||
|
compile compressCssCompiler
|
||||||
|
|
||||||
|
match "error/*" $ do
|
||||||
|
route $ (gsubRoute "error/" (const "") `composeRoutes` setExtension "html")
|
||||||
|
compile $ pandocCompiler
|
||||||
|
>>= applyAsTemplate siteCtx
|
||||||
|
>>= loadAndApplyTemplate "templates/default.html" (baseSidebarCtx <> siteCtx)
|
||||||
|
|
||||||
|
match "pages/*" $ do
|
||||||
|
route $ setExtension "html"
|
||||||
|
compile $ do
|
||||||
|
pageName <- takeBaseName . toFilePath <$> getUnderlying
|
||||||
|
let pageCtx = constField pageName "" `mappend`
|
||||||
|
baseNodeCtx
|
||||||
|
let evalCtx = functionField "get-meta" getMetadataKey `mappend`
|
||||||
|
functionField "eval" (evalCtxKey pageCtx)
|
||||||
|
let activeSidebarCtx = sidebarCtx (evalCtx <> pageCtx)
|
||||||
|
|
||||||
|
pandocCompiler
|
||||||
|
>>= saveSnapshot "page-content"
|
||||||
|
>>= loadAndApplyTemplate "templates/page.html" siteCtx
|
||||||
|
>>= loadAndApplyTemplate "templates/default.html" (activeSidebarCtx <> siteCtx)
|
||||||
|
>>= relativizeUrls
|
||||||
|
|
||||||
|
tags <- buildTags "posts/*" (fromCapture "tags/*.html")
|
||||||
|
|
||||||
|
match "posts/*" $ version "meta" $ do
|
||||||
|
route $ setExtension "html"
|
||||||
|
compile getResourceBody
|
||||||
|
|
||||||
|
match "posts/*" $ do
|
||||||
|
route $ setExtension "html"
|
||||||
|
compile $ do
|
||||||
|
posts <- loadAll ("posts/*" .&&. hasVersion "meta")
|
||||||
|
let taggedPostCtx = (tagsField "tags" tags) `mappend`
|
||||||
|
postCtx `mappend`
|
||||||
|
(relatedPostsCtx posts 3)
|
||||||
|
|
||||||
|
pandocCompiler
|
||||||
|
>>= saveSnapshot "content"
|
||||||
|
>>= loadAndApplyTemplate "templates/post.html" taggedPostCtx
|
||||||
|
>>= loadAndApplyTemplate "templates/default.html" (baseSidebarCtx <> siteCtx)
|
||||||
|
>>= relativizeUrls
|
||||||
|
|
||||||
|
create ["archive.html"] $ do
|
||||||
|
route idRoute
|
||||||
|
compile $ do
|
||||||
|
posts <- recentFirst =<< loadAllSnapshots ("posts/*" .&&. hasNoVersion) "content"
|
||||||
|
let archiveCtx =
|
||||||
|
listField "posts" postCtx (return posts) `mappend`
|
||||||
|
constField "title" "Archive" `mappend`
|
||||||
|
constField "archive" "" `mappend`
|
||||||
|
siteCtx
|
||||||
|
|
||||||
|
makeItem ""
|
||||||
|
>>= loadAndApplyTemplate "templates/archive.html" archiveCtx
|
||||||
|
>>= loadAndApplyTemplate "templates/default.html" (baseSidebarCtx <> archiveCtx)
|
||||||
|
>>= relativizeUrls
|
||||||
|
|
||||||
|
paginate <- buildPaginateWith postsGrouper "posts/*" postsPageId
|
||||||
|
|
||||||
|
paginateRules paginate $ \page pattern -> do
|
||||||
|
route idRoute
|
||||||
|
compile $ do
|
||||||
|
posts <- recentFirst =<< loadAllSnapshots (pattern .&&. hasNoVersion) "content"
|
||||||
|
let indexCtx =
|
||||||
|
constField "title" (if page == 1 then "Home"
|
||||||
|
else "Blog posts, page " ++ show page) `mappend`
|
||||||
|
listField "posts" postCtx (return posts) `mappend`
|
||||||
|
constField "home" "" `mappend`
|
||||||
|
paginateContext paginate page `mappend`
|
||||||
|
siteCtx
|
||||||
|
|
||||||
|
makeItem ""
|
||||||
|
>>= applyAsTemplate indexCtx
|
||||||
|
>>= loadAndApplyTemplate "templates/index.html" indexCtx
|
||||||
|
>>= loadAndApplyTemplate "templates/default.html" (baseSidebarCtx <> indexCtx)
|
||||||
|
>>= relativizeUrls
|
||||||
|
|
||||||
|
match "templates/*" $ compile templateBodyCompiler
|
||||||
|
|
||||||
|
create ["atom.xml"] $ do
|
||||||
|
route idRoute
|
||||||
|
compile $ do
|
||||||
|
let feedCtx = postCtx `mappend`
|
||||||
|
bodyField "description"
|
||||||
|
posts <- fmap (take 10) . recentFirst =<< loadAllSnapshots ("posts/*" .&&. hasNoVersion) "content"
|
||||||
|
renderAtom feedConfig feedCtx posts
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
postsGrouper :: MonadFail m => MonadMetadata m => [Identifier] -> m [[Identifier]]
|
||||||
|
postsGrouper = liftM (paginateEvery 3) . sortRecentFirst
|
||||||
|
|
||||||
|
|
||||||
|
postsPageId :: PageNumber -> Identifier
|
||||||
|
postsPageId n = fromFilePath $ if (n == 1) then "index.html" else show n ++ "/index.html"
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
feedConfig :: FeedConfiguration
|
||||||
|
feedConfig = FeedConfiguration
|
||||||
|
{ feedTitle = "Voices of Capricornia"
|
||||||
|
, feedDescription = "Be Heard"
|
||||||
|
, feedAuthorName = "Voices of Capricornia"
|
||||||
|
, feedAuthorEmail = "collective@voicesofcapricornia.org"
|
||||||
|
, feedRoot = "https://voicesofcapricornia.org"
|
||||||
|
}
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
siteCtx :: Context String
|
||||||
|
siteCtx =
|
||||||
|
baseCtx `mappend`
|
||||||
|
constField "site_description" "Voices of Capricornia" `mappend`
|
||||||
|
constField "site-url" "https://voicesofcapricornia.org" `mappend`
|
||||||
|
constField "tagline" "Be Heard" `mappend`
|
||||||
|
constField "site-title" "Voices of Capricornia" `mappend`
|
||||||
|
constField "copy-year" "2021" `mappend`
|
||||||
|
constField "github-repo" "https://source.jfdic.org/voc/voc-web" `mappend`
|
||||||
|
defaultContext
|
||||||
|
|
||||||
|
baseCtx =
|
||||||
|
constField "baseurl" "https://voicesofcapricornia.org"
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
postCtx :: Context String
|
||||||
|
postCtx =
|
||||||
|
dateField "date" "%B %e, %Y" `mappend`
|
||||||
|
defaultContext
|
||||||
|
|
||||||
|
tagsRulesVersioned tags rules =
|
||||||
|
forM_ (tagsMap tags) $ \(tag, identifiers) ->
|
||||||
|
rulesExtraDependencies [tagsDependency tags] $
|
||||||
|
create [tagsMakeId tags tag] $
|
||||||
|
rules tag identifiers
|
||||||
|
|
||||||
|
relatedPostsCtx
|
||||||
|
:: [Item String] -> Int -> Context String
|
||||||
|
relatedPostsCtx posts n = listFieldWith "related_posts" postCtx selectPosts
|
||||||
|
where
|
||||||
|
rateItem ts i = length . filter (`elem` ts) <$> (getTags $ itemIdentifier i)
|
||||||
|
selectPosts s = do
|
||||||
|
postTags <- getTags $ itemIdentifier s
|
||||||
|
let trimmedItems = filter (not . matchPath s) posts
|
||||||
|
take n . reverse <$> sortOnM (rateItem postTags) trimmedItems
|
||||||
|
|
||||||
|
matchPath :: Item String -> Item String -> Bool
|
||||||
|
matchPath x y = eqOn (toFilePath . itemIdentifier) x y
|
||||||
|
|
||||||
|
eqOn :: Eq b => (a -> b) -> a -> a -> Bool
|
||||||
|
eqOn f x y = f x == f y
|
||||||
|
|
||||||
|
sortOnM :: (Monad m, Ord b) => (a -> m b) -> [a] -> m [a]
|
||||||
|
sortOnM f xs = map fst . sortBy (comparing snd) . zip xs <$> mapM f xs
|
||||||
|
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
sidebarCtx :: Context String -> Context String
|
||||||
|
sidebarCtx nodeCtx =
|
||||||
|
listField "list_pages" nodeCtx (loadAllSnapshots ("pages/*" .&&. hasNoVersion) "page-content") `mappend`
|
||||||
|
defaultContext
|
||||||
|
|
||||||
|
baseNodeCtx :: Context String
|
||||||
|
baseNodeCtx =
|
||||||
|
urlField "node-url" `mappend`
|
||||||
|
titleField "title" `mappend`
|
||||||
|
baseCtx
|
||||||
|
|
||||||
|
baseSidebarCtx = sidebarCtx baseNodeCtx
|
||||||
|
|
||||||
|
evalCtxKey :: Context String -> [String] -> Item String -> Compiler String
|
||||||
|
evalCtxKey context [key] item = (unContext context key [] item) >>= \cf ->
|
||||||
|
case cf of
|
||||||
|
StringField s -> return s
|
||||||
|
_ -> error $ "Internal error: StringField expected"
|
||||||
|
|
||||||
|
getMetadataKey :: [String] -> Item String -> Compiler String
|
||||||
|
getMetadataKey [key] item = getMetadataField' (itemIdentifier item) key
|
14
voc-web.cabal
Normal file
14
voc-web.cabal
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
name: voc-web
|
||||||
|
version: 0.1.0.0
|
||||||
|
build-type: Simple
|
||||||
|
cabal-version: >= 1.10
|
||||||
|
|
||||||
|
executable site
|
||||||
|
main-is: site.hs
|
||||||
|
build-depends: base == 4.*
|
||||||
|
, hakyll == 4.14.*
|
||||||
|
, containers == 0.6.*
|
||||||
|
, filepath == 1.4.*
|
||||||
|
, pandoc
|
||||||
|
ghc-options: -threaded
|
||||||
|
default-language: Haskell2010
|
Loading…
Reference in a new issue