{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
module Imm.Hooks.WriteFile (module Imm.Hooks.WriteFile, module Imm.Hooks) where
import Imm.Feed
import Imm.Hooks
import Imm.Pretty
import Data.ByteString.Builder
import Data.ByteString.Streaming (toStreamingByteString)
import qualified Data.Text as Text (null, replace)
import Data.Time
import Streaming.With
import System.Directory (createDirectoryIfMissing)
import System.FilePath
import Text.Atom.Types
import Text.Blaze.Html.Renderer.Utf8
import Text.Blaze.Html5 (Html, docTypeHtml,
preEscapedToHtml, (!))
import qualified Text.Blaze.Html5 as H
import qualified Text.Blaze.Html5.Attributes as H (charset, href)
import Text.RSS.Types
import URI.ByteString
data FileInfo = FileInfo FilePath Builder
newtype WriteFileSettings = WriteFileSettings (Feed -> FeedElement -> FileInfo)
mkHandle :: MonadBase IO m => MonadIO m => MonadMask m => WriteFileSettings -> Handle m
mkHandle (WriteFileSettings f) = Handle
{ processNewElement = \feed element -> do
let FileInfo path content = f feed element
liftBase $ createDirectoryIfMissing True $ takeDirectory path
writeBinaryFile path $ toStreamingByteString content
}
defaultSettings :: FilePath
-> WriteFileSettings
defaultSettings root = WriteFileSettings $ \feed element -> FileInfo
(defaultFilePath root feed element)
(defaultFileContent feed element)
defaultFilePath :: FilePath -> Feed -> FeedElement -> FilePath
defaultFilePath root feed element = makeValid $ root </> toString title </> fileName <.> "html" where
date = maybe "" (formatTime defaultTimeLocale "%F-") $ getDate element
fileName = date <> toString (sanitize $ getTitle element)
title = sanitize $ getFeedTitle feed
sanitize = appEndo (mconcat [Endo $ Text.replace (toText [s]) "_" | s <- pathSeparators])
>>> Text.replace "." "_"
>>> Text.replace "?" "_"
>>> Text.replace "!" "_"
>>> Text.replace "#" "_"
defaultFileContent :: Feed -> FeedElement -> Builder
defaultFileContent feed element = renderHtmlBuilder $ docTypeHtml $ do
H.head $ do
H.meta ! H.charset "utf-8"
H.title $ convertText $ getFeedTitle feed <> " | " <> getTitle element
H.body $ do
H.h1 $ convertText $ getFeedTitle feed
H.article $ do
H.header $ do
defaultArticleTitle feed element
defaultArticleAuthor feed element
defaultArticleDate feed element
defaultBody feed element
defaultArticleTitle :: Feed -> FeedElement -> Html
defaultArticleTitle _ element@(RssElement item) = H.h2 $ maybe id (\uri -> H.a ! H.href uri) link $ convertText $ getTitle element where
link = withRssURI (convertDoc . prettyURI) <$> itemLink item
defaultArticleTitle _ element@(AtomElement _) = H.h2 $ convertText $ getTitle element
defaultArticleAuthor :: Feed -> FeedElement -> Html
defaultArticleAuthor _ (RssElement item) = unless (Text.null author) $ H.address $ "Published by " >> convertText author where
author = itemAuthor item
defaultArticleAuthor _ (AtomElement entry) = H.address $ do
"Published by "
forM_ (entryAuthors entry) $ \author -> do
convertDoc $ prettyPerson author
", "
defaultArticleDate :: Feed -> FeedElement -> Html
defaultArticleDate _ element = forM_ (getDate element) $ \date -> H.p $ " on " >> H.time (convertDoc $ prettyTime date)
defaultBody :: Feed -> FeedElement -> Html
defaultBody _ element@(RssElement _) = H.p $ preEscapedToHtml $ getContent element
defaultBody _ element@(AtomElement entry) = do
unless (null links) $ H.p $ do
"Related links:"
H.ul $ forM_ links $ \uri -> H.li (H.a ! withAtomURI href uri $ convertAtomURI uri)
H.p $ preEscapedToHtml $ getContent element
where links = map linkHref $ entryLinks entry
href :: URIRef a -> H.Attribute
href = H.href . convertURI
convertAtomURI :: (IsString t) => AtomURI -> t
convertAtomURI = withAtomURI convertURI
convertURI :: (IsString t) => URIRef a -> t
convertURI = convertText . decodeUtf8 . serializeURIRef'
convertText :: (IsString t) => Text -> t
convertText = fromString . toString
convertDoc :: (IsString t) => Doc a -> t
convertDoc = show