mathblog-0.6: src/MB/Types.hs
module MB.Types
( Blog(..)
, StartupConfig(..)
, BlogM
, GenEvent(..)
, GenState(..)
, Post(..)
, Template
, ChangeSummary(..)
, Page(..)
, Processor(..)
, RenderCause(..)
, BlogInputFS(..)
, BlogOutputFS(..)
, BlogInputFSState(..)
, nullProcessor
, noChanges
, theBlog
, theConfig
, notify
, runBlogM
, blogOutputFS
, blogInputFS
)
where
import Control.Concurrent.Chan
import Control.Monad.Reader
import Data.Time.Clock (UTCTime)
import Data.Monoid
import Data.List (nub)
import System.FilePath ((</>))
import Network.Socket (HostName, PortNumber)
import qualified Text.Pandoc as Pandoc
import Text.StringTemplate
( StringTemplate
)
data StartupConfig = StartupConfig { dataDirectory :: FilePath
, initDataDirectory :: Bool
, forceRegeneration :: Bool
, htmlOutputDirectory :: FilePath
, configFilePath :: FilePath
, listenAddr :: Maybe (HostName, PortNumber)
, overrideBaseUrl :: Maybe String
}
deriving (Show, Eq)
blogOutputFS :: StartupConfig -> BlogOutputFS
blogOutputFS c =
let base = htmlOutputDirectory c
images = base </> "generated-images"
posts = base </> "posts"
in BlogOutputFS { ofsBaseDir = base
, ofsPostHtmlDir = posts
, ofsImageDir = images
, ofsHtmlTempDir = base </> "tmp"
, ofsImagePath = \fn -> images </> fn
, ofsIndexHtml = base </> "index.html"
, ofsRssXml = base </> "feed.xml"
, ofsListHtml = posts </> "index.html"
, ofsPostFinalHtml = \p -> posts </> postHtmlFilename p
}
blogInputFS :: StartupConfig -> BlogInputFS
blogInputFS c =
let base = dataDirectory c
templates = base </> "templates"
posts = base </> "posts"
in BlogInputFS { ifsBaseDir = base
, ifsPostSourceDir = posts
, ifsTemplateDir = templates
, ifsPostIndexPath = posts </> "posts-index"
, ifsConfigPath = base </> "blog.cfg"
, ifsAssetDir = base </> "assets"
, ifsPageTemplatePath = templates </> "pageTemplate.html"
, ifsListTemplatePath = templates </> "listTemplate.html"
, ifsPostTemplatePath = templates </> "postTemplate.html"
, ifsRssTemplatePath = templates </> "rssTemplate.xml"
}
data GenState =
GenState { stBlog :: Blog
, stChan :: Chan GenEvent
, stConfig :: StartupConfig
}
data GenEvent = PostRender Post [RenderCause]
| Finished
data RenderCause = Config
| PostIndex
| Template
| PostModified
| Forced
deriving (Eq, Show)
type BlogM a = ReaderT GenState IO a
theBlog :: BlogM Blog
theBlog = asks stBlog
theConfig :: BlogM StartupConfig
theConfig = asks stConfig
notify :: GenEvent -> BlogM ()
notify ev = do
ch <- asks stChan
liftIO $ writeChan ch ev
runBlogM :: Blog -> Chan GenEvent -> StartupConfig -> BlogM a -> IO a
runBlogM b ch conf act = runReaderT act (GenState b ch conf)
type Template = StringTemplate String
data BlogInputFS =
BlogInputFS { ifsBaseDir :: FilePath
, ifsPostSourceDir :: FilePath
, ifsAssetDir :: FilePath
, ifsTemplateDir :: FilePath
, ifsConfigPath :: FilePath
, ifsPostIndexPath :: FilePath
, ifsPageTemplatePath :: FilePath
, ifsListTemplatePath :: FilePath
, ifsPostTemplatePath :: FilePath
, ifsRssTemplatePath :: FilePath
}
data BlogOutputFS =
BlogOutputFS { ofsBaseDir :: FilePath
, ofsPostHtmlDir :: FilePath
, ofsImageDir :: FilePath
, ofsHtmlTempDir :: FilePath
, ofsImagePath :: String -> FilePath
, ofsIndexHtml :: FilePath
, ofsRssXml :: FilePath
, ofsListHtml :: FilePath
, ofsPostFinalHtml :: Post -> FilePath
}
data BlogInputFSState =
BlogInputFSState { ifsPostIndexMTime :: UTCTime
, ifsConfigMTime :: UTCTime
, ifsBaselineMTime :: UTCTime
, ifsTemplateMTime :: UTCTime
}
data Blog = Blog { inputFS :: BlogInputFS
, outputFS :: BlogOutputFS
, inputFSState :: BlogInputFSState
, baseUrl :: String
, title :: String
, authorName :: String
, authorEmail :: String
, blogPosts :: [Post]
, processors :: [Processor]
}
data Post = Post { postTitle :: [Pandoc.Inline]
, postPath :: String
, postFilename :: String
, postModificationTime :: UTCTime
, postModificationString :: String
, postAst :: Pandoc.Pandoc
, postTeXMacros :: String
, postAuthors :: [String]
, postDate :: Maybe String
, postBaseName :: String
, postUrl :: String
, postHtmlFilename :: String
}
deriving (Eq)
data Page = BlogPost
| Index
data Processor =
Processor { applyWriterOptions :: Maybe (Pandoc.WriterOptions -> Pandoc.WriterOptions)
, preProcessPost :: Maybe (Post -> BlogM Post)
, postProcessPost :: Maybe (FilePath -> Page -> BlogM ())
, pageHead :: Maybe String
, buildPostTitle :: Maybe (Page -> [Pandoc.Inline] -> [Pandoc.Inline])
, rawPostTitle :: Maybe ([Pandoc.Inline] -> String)
, checkDataDir :: Maybe (BlogM ())
, installAssets :: Maybe (BlogM ())
}
nullProcessor :: Processor
nullProcessor =
Processor Nothing Nothing Nothing Nothing Nothing Nothing Nothing Nothing
-- Summarize changes in files so we know what to do during the
-- regeneration phase. postsChanged and configChanged are the primary
-- measurements, but knowing whether other metadata files changed
-- (like templates) is useful for the "listen" mode of mb.
data ChangeSummary =
ChangeSummary { postsChanged :: [String]
, configChanged :: Bool
, templatesChanged :: [FilePath]
, postIndexChanged :: Bool
, assetsChanged :: [FilePath]
}
deriving (Show)
noChanges :: ChangeSummary
noChanges = ChangeSummary [] False [] False []
instance Monoid ChangeSummary where
mempty = noChanges
a `mappend` b =
ChangeSummary { postsChanged = nub $ postsChanged a ++ postsChanged b
, configChanged = configChanged a || configChanged b
, templatesChanged = templatesChanged a `mappend` templatesChanged b
, postIndexChanged = postIndexChanged a || postIndexChanged b
, assetsChanged = assetsChanged a `mappend` assetsChanged b
}