Take a part of Blog's configuration into a separate Skin module, add syntactic sugar and use the result in generation modules
This commit is contained in:
parent
7beb159a24
commit
515fb14914
6 changed files with 81 additions and 27 deletions
|
@ -23,12 +23,14 @@ executable hablo
|
||||||
, Article
|
, Article
|
||||||
, ArticlesList
|
, ArticlesList
|
||||||
, Blog
|
, Blog
|
||||||
|
, Blog.Skin
|
||||||
, Dom
|
, Dom
|
||||||
, Files
|
, Files
|
||||||
, HTML
|
, HTML
|
||||||
, JS
|
, JS
|
||||||
, JSON
|
, JSON
|
||||||
, Paths_hablo
|
, Paths_hablo
|
||||||
|
, Pretty
|
||||||
-- other-extensions:
|
-- other-extensions:
|
||||||
build-depends: aeson
|
build-depends: aeson
|
||||||
, base ^>=4.12.0.0
|
, base ^>=4.12.0.0
|
||||||
|
|
25
src/Blog.hs
25
src/Blog.hs
|
@ -2,14 +2,17 @@
|
||||||
{-# LANGUAGE FlexibleContexts #-}
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
module Blog (
|
module Blog (
|
||||||
Blog(..)
|
Blog(..)
|
||||||
|
, Skin(..)
|
||||||
, build
|
, build
|
||||||
, get
|
, get
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Arguments (Arguments(bannerPath, blogName, headPath, sourceDir))
|
import Arguments (Arguments(sourceDir))
|
||||||
import qualified Arguments (previewArticlesCount, previewLinesCount)
|
import qualified Arguments (name)
|
||||||
import Article (Article)
|
import Article (Article)
|
||||||
import qualified Article (at)
|
import qualified Article (at)
|
||||||
|
import Blog.Skin (Skin(..))
|
||||||
|
import qualified Blog.Skin as Skin (build)
|
||||||
import Control.Monad ((>=>), filterM, forM)
|
import Control.Monad ((>=>), filterM, forM)
|
||||||
import Control.Monad.Reader (MonadReader, ask)
|
import Control.Monad.Reader (MonadReader, ask)
|
||||||
import Data.Map (Map)
|
import Data.Map (Map)
|
||||||
|
@ -27,12 +30,9 @@ type Collection = Map FileID Article
|
||||||
data Blog = Blog {
|
data Blog = Blog {
|
||||||
articles :: Collection
|
articles :: Collection
|
||||||
, name :: String
|
, name :: String
|
||||||
, previewArticlesCount :: Int
|
|
||||||
, previewLinesCount :: Int
|
|
||||||
, root :: FilePath
|
, root :: FilePath
|
||||||
|
, skin :: Skin
|
||||||
, tags :: Map String (Set FileID)
|
, tags :: Map String (Set FileID)
|
||||||
, customBanner :: Maybe String
|
|
||||||
, customHead :: Maybe String
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get :: MonadReader Blog m => (Blog -> a) -> m a
|
get :: MonadReader Blog m => (Blog -> a) -> m a
|
||||||
|
@ -63,22 +63,17 @@ tagged collection path = do
|
||||||
|
|
||||||
build :: Arguments -> IO Blog
|
build :: Arguments -> IO Blog
|
||||||
build arguments = withCurrentDirectory root $ do
|
build arguments = withCurrentDirectory root $ do
|
||||||
let name = maybe (takeFileName $ dropTrailingPathSeparator root) id $ blogName arguments
|
let name = maybe (takeFileName $ dropTrailingPathSeparator root) id $ Arguments.name arguments
|
||||||
let previewLinesCount = Arguments.previewLinesCount arguments
|
skin <- Skin.build name arguments
|
||||||
articles <- findArticles previewLinesCount articlesPath
|
articles <- findArticles (previewLinesCount skin) articlesPath
|
||||||
tags <- Map.fromList . filter (not . Set.null . snd)
|
tags <- Map.fromList . filter (not . Set.null . snd)
|
||||||
<$> (Files.find (articlesPath </> "tags") >>= mapM (articles `tagged`))
|
<$> (Files.find (articlesPath </> "tags") >>= mapM (articles `tagged`))
|
||||||
customBanner <- mapM readFile $ bannerPath arguments
|
|
||||||
customHead <- mapM readFile $ headPath arguments
|
|
||||||
return $ Blog {
|
return $ Blog {
|
||||||
articles
|
articles
|
||||||
, name
|
, name
|
||||||
, previewArticlesCount = Arguments.previewArticlesCount arguments
|
|
||||||
, previewLinesCount
|
|
||||||
, root
|
, root
|
||||||
|
, skin
|
||||||
, tags
|
, tags
|
||||||
, customBanner
|
|
||||||
, customHead
|
|
||||||
}
|
}
|
||||||
where
|
where
|
||||||
(root, articlesPath) = splitFileName $ sourceDir arguments
|
(root, articlesPath) = splitFileName $ sourceDir arguments
|
||||||
|
|
49
src/Blog/Skin.hs
Normal file
49
src/Blog/Skin.hs
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
{-# LANGUAGE NamedFieldPuns #-}
|
||||||
|
module Blog.Skin (
|
||||||
|
Skin(..)
|
||||||
|
, build
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Arguments (Arguments)
|
||||||
|
import qualified Arguments (bannerPath, favicon, cardImage, headPath, previewArticlesCount, previewLinesCount)
|
||||||
|
import Control.Monad (filterM)
|
||||||
|
import Data.Maybe (listToMaybe)
|
||||||
|
import Prelude hiding (head)
|
||||||
|
import System.Directory (doesFileExist)
|
||||||
|
import System.FilePath ((</>), (<.>))
|
||||||
|
|
||||||
|
data Skin = Skin {
|
||||||
|
banner :: Maybe String
|
||||||
|
, cardImage :: FilePath
|
||||||
|
, favicon :: Maybe FilePath
|
||||||
|
, head :: Maybe String
|
||||||
|
, previewArticlesCount :: Int
|
||||||
|
, previewLinesCount :: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
findFavicon :: Arguments -> IO (Maybe FilePath)
|
||||||
|
findFavicon arguments =
|
||||||
|
case Arguments.favicon arguments of
|
||||||
|
Just path -> return $ Just path
|
||||||
|
_ -> fmap absolute . listToMaybe <$> filterM doesFileExist pathsToCheck
|
||||||
|
where
|
||||||
|
directories = [".", "image", "images", "pictures", "skin", "static"]
|
||||||
|
extensions = ["ico", "gif", "jpeg", "jpg", "png", "svg"]
|
||||||
|
pathsToCheck = [ dir </> "favicon" <.> ext | dir <- directories, ext <- extensions ]
|
||||||
|
absolute ('.':path) = path
|
||||||
|
absolute path = "/" </> path
|
||||||
|
|
||||||
|
build :: String -> Arguments -> IO Skin
|
||||||
|
build blogName arguments = do
|
||||||
|
let cardImage = maybe ("/image" </> blogName <.> "png") id $ Arguments.cardImage arguments
|
||||||
|
banner <- mapM readFile $ Arguments.bannerPath arguments
|
||||||
|
favicon <- findFavicon arguments
|
||||||
|
head <- mapM readFile $ Arguments.headPath arguments
|
||||||
|
return $ Skin {
|
||||||
|
banner
|
||||||
|
, cardImage
|
||||||
|
, favicon
|
||||||
|
, head
|
||||||
|
, previewArticlesCount = Arguments.previewArticlesCount arguments
|
||||||
|
, previewLinesCount = Arguments.previewLinesCount arguments
|
||||||
|
}
|
21
src/Dom.hs
21
src/Dom.hs
|
@ -6,7 +6,7 @@ module Dom (
|
||||||
|
|
||||||
import Article (Article(..))
|
import Article (Article(..))
|
||||||
import ArticlesList (ArticlesList(..), otherLink, otherUrl, pageTitle)
|
import ArticlesList (ArticlesList(..), otherLink, otherUrl, pageTitle)
|
||||||
import Blog (Blog(..))
|
import Blog (Blog(..), Skin(..))
|
||||||
import qualified Blog (get)
|
import qualified Blog (get)
|
||||||
import Control.Monad.Reader (ReaderT)
|
import Control.Monad.Reader (ReaderT)
|
||||||
import qualified Data.Map as Map (keys)
|
import qualified Data.Map as Map (keys)
|
||||||
|
@ -14,6 +14,8 @@ import Data.Monoid ((<>))
|
||||||
import Data.Text (pack, empty)
|
import Data.Text (pack, empty)
|
||||||
import Lucid
|
import Lucid
|
||||||
import Lucid.Base (makeAttribute)
|
import Lucid.Base (makeAttribute)
|
||||||
|
import Prelude hiding (head)
|
||||||
|
import Pretty ((.$))
|
||||||
import System.FilePath.Posix ((</>), (<.>))
|
import System.FilePath.Posix ((</>), (<.>))
|
||||||
|
|
||||||
type HtmlGenerator = HtmlT (ReaderT Blog IO)
|
type HtmlGenerator = HtmlT (ReaderT Blog IO)
|
||||||
|
@ -45,11 +47,10 @@ instance Page ArticlesList where
|
||||||
|
|
||||||
makeCard :: String -> String -> HtmlGenerator ()
|
makeCard :: String -> String -> HtmlGenerator ()
|
||||||
makeCard title description = do
|
makeCard title description = do
|
||||||
blogName <- Blog.get name
|
|
||||||
og "title" title
|
og "title" title
|
||||||
og "description" description
|
og "description" description
|
||||||
og "image" $ "/image" </> blogName <.> "png"
|
og "image" =<< (Blog.get $skin.$cardImage)
|
||||||
og "site_name" blogName
|
og "site_name" =<< Blog.get name
|
||||||
where
|
where
|
||||||
og attribute value = meta_ [makeAttribute "property" $ "og:" <> attribute , content_ $ pack value]
|
og attribute value = meta_ [makeAttribute "property" $ "og:" <> attribute , content_ $ pack value]
|
||||||
|
|
||||||
|
@ -66,10 +67,6 @@ previewArticle (Article {urlPath, title, preview}) =
|
||||||
tag :: String -> HtmlGenerator ()
|
tag :: String -> HtmlGenerator ()
|
||||||
tag tagName = li_ (navigationA [href_ $ pack ("/" </> tagName)] $ toHtml tagName)
|
tag tagName = li_ (navigationA [href_ $ pack ("/" </> tagName)] $ toHtml tagName)
|
||||||
|
|
||||||
banner :: HtmlGenerator ()
|
|
||||||
banner = do
|
|
||||||
maybe defaultBanner toHtmlRaw =<< Blog.get customBanner
|
|
||||||
|
|
||||||
defaultBanner :: HtmlGenerator ()
|
defaultBanner :: HtmlGenerator ()
|
||||||
defaultBanner = do
|
defaultBanner = do
|
||||||
div_ [id_ "header"] (
|
div_ [id_ "header"] (
|
||||||
|
@ -78,6 +75,9 @@ defaultBanner = do
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
faviconLink :: FilePath -> HtmlGenerator ()
|
||||||
|
faviconLink url = link_ [rel_ "shortcut icon", href_ $ pack url, type_ "image/x-icon"]
|
||||||
|
|
||||||
page :: Page a => a -> HtmlGenerator ()
|
page :: Page a => a -> HtmlGenerator ()
|
||||||
page aPage =
|
page aPage =
|
||||||
doctypehtml_ (do
|
doctypehtml_ (do
|
||||||
|
@ -87,11 +87,12 @@ page aPage =
|
||||||
script_ [src_ "/UnitJS/async.js"] empty
|
script_ [src_ "/UnitJS/async.js"] empty
|
||||||
script_ [src_ "/UnitJS/dom.js"] empty
|
script_ [src_ "/UnitJS/dom.js"] empty
|
||||||
script_ [src_ "/js/hablo.js"] empty
|
script_ [src_ "/js/hablo.js"] empty
|
||||||
|
maybe (toHtml empty) faviconLink =<< (Blog.get $skin.$favicon)
|
||||||
card aPage
|
card aPage
|
||||||
maybe (toHtml empty) toHtmlRaw =<< Blog.get customHead
|
(Blog.get $skin.$head) >>= maybe (toHtml empty) toHtmlRaw
|
||||||
)
|
)
|
||||||
body_ (do
|
body_ (do
|
||||||
banner
|
(Blog.get $skin.$banner) >>= maybe defaultBanner toHtmlRaw
|
||||||
div_ [id_ "navigator"] (do
|
div_ [id_ "navigator"] (do
|
||||||
h2_ "Tags"
|
h2_ "Tags"
|
||||||
ul_ . mapM_ tag . Map.keys =<< Blog.get tags
|
ul_ . mapM_ tag . Map.keys =<< Blog.get tags
|
||||||
|
|
|
@ -6,7 +6,7 @@ module HTML (
|
||||||
|
|
||||||
import Article(Article(..))
|
import Article(Article(..))
|
||||||
import ArticlesList (ArticlesList(..))
|
import ArticlesList (ArticlesList(..))
|
||||||
import Blog (Blog(..))
|
import Blog (Blog(..), Skin(..))
|
||||||
import qualified Blog (get)
|
import qualified Blog (get)
|
||||||
import Control.Monad (forM)
|
import Control.Monad (forM)
|
||||||
import Control.Monad.IO.Class (MonadIO(..))
|
import Control.Monad.IO.Class (MonadIO(..))
|
||||||
|
@ -18,6 +18,7 @@ import qualified Data.Set as Set (member)
|
||||||
import qualified Data.Text.Lazy.IO as TextIO (writeFile)
|
import qualified Data.Text.Lazy.IO as TextIO (writeFile)
|
||||||
import Dom (page)
|
import Dom (page)
|
||||||
import Lucid
|
import Lucid
|
||||||
|
import Pretty ((.$))
|
||||||
import System.Directory (createDirectoryIfMissing)
|
import System.Directory (createDirectoryIfMissing)
|
||||||
import System.FilePath.Posix ((</>), (<.>))
|
import System.FilePath.Posix ((</>), (<.>))
|
||||||
import System.Posix.Files (modificationTime)
|
import System.Posix.Files (modificationTime)
|
||||||
|
@ -41,7 +42,7 @@ collection articlesFeatured tag = do
|
||||||
|
|
||||||
articlesLists :: Monad m => Collection -> ReaderT Blog m [(FilePath, ArticlesList)]
|
articlesLists :: Monad m => Collection -> ReaderT Blog m [(FilePath, ArticlesList)]
|
||||||
articlesLists (Collection {articlesFeatured, basePath, tag}) = do
|
articlesLists (Collection {articlesFeatured, basePath, tag}) = do
|
||||||
limit <- take <$> Blog.get previewArticlesCount
|
limit <- take <$> (Blog.get $skin.$previewArticlesCount)
|
||||||
return [
|
return [
|
||||||
(basePath </> "index.html", ArticlesList {
|
(basePath </> "index.html", ArticlesList {
|
||||||
tagged = tag
|
tagged = tag
|
||||||
|
|
6
src/Pretty.hs
Normal file
6
src/Pretty.hs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
module Pretty (
|
||||||
|
(.$)
|
||||||
|
) where
|
||||||
|
|
||||||
|
(.$) :: (a -> b) -> (b -> c) -> (a -> c)
|
||||||
|
(.$) f g = g . f
|
Loading…
Reference in a new issue