Replace naive String implementation of URL by a proper type from an existing library; thus validating URLs handled by hablo
This commit is contained in:
parent
0989b4cab3
commit
6a79533634
7 changed files with 89 additions and 41 deletions
|
@ -65,6 +65,7 @@ library
|
||||||
, time >= 1.8.0 && < 1.12
|
, time >= 1.8.0 && < 1.12
|
||||||
, SJW >= 0.1.2 && < 0.2
|
, SJW >= 0.1.2 && < 0.2
|
||||||
, unix >= 2.7.2 && < 2.8
|
, unix >= 2.7.2 && < 2.8
|
||||||
|
, url >= 2.1.3 && < 2.2
|
||||||
ghc-options: -Wall
|
ghc-options: -Wall
|
||||||
hs-source-dirs: src
|
hs-source-dirs: src
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
|
@ -107,6 +108,7 @@ test-suite tests
|
||||||
, lucid
|
, lucid
|
||||||
, mtl
|
, mtl
|
||||||
, text
|
, text
|
||||||
|
, url
|
||||||
hs-source-dirs: test
|
hs-source-dirs: test
|
||||||
ghc-options: -Wall
|
ghc-options: -Wall
|
||||||
default-language: Haskell2010
|
default-language: Haskell2010
|
||||||
|
|
|
@ -6,6 +6,7 @@ module ArticlesList (
|
||||||
, description
|
, description
|
||||||
, getArticles
|
, getArticles
|
||||||
, otherURL
|
, otherURL
|
||||||
|
, ArticlesList.path
|
||||||
, rssLinkTexts
|
, rssLinkTexts
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
@ -29,8 +30,8 @@ getArticles (ArticlesList {full, collection = Collection {featured}}) = do
|
||||||
return $ if full then featured else limit featured
|
return $ if full then featured else limit featured
|
||||||
|
|
||||||
otherURL :: ArticlesList -> String
|
otherURL :: ArticlesList -> String
|
||||||
otherURL (ArticlesList {full, collection}) = absoluteLink $
|
otherURL al@(ArticlesList {full}) = absoluteLink $
|
||||||
(if full then id else (</> "all.html")) . maybe "" id $ tag collection
|
(if full then id else (</> "all.html")) $ ArticlesList.path al
|
||||||
|
|
||||||
description :: Renderer m => ArticlesList -> m Text
|
description :: Renderer m => ArticlesList -> m Text
|
||||||
description (ArticlesList {full, collection}) =
|
description (ArticlesList {full, collection}) =
|
||||||
|
@ -46,3 +47,6 @@ rssLinkTexts (ArticlesList {collection}) = do
|
||||||
return (text, title)
|
return (text, title)
|
||||||
where
|
where
|
||||||
environment = maybe [] (\v -> [("tag", pack v)]) $ tag collection
|
environment = maybe [] (\v -> [("tag", pack v)]) $ tag collection
|
||||||
|
|
||||||
|
path :: ArticlesList -> FilePath
|
||||||
|
path = maybe "" id . tag . collection
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
{-# LANGUAGE DeriveGeneric #-}
|
|
||||||
{-# LANGUAGE NamedFieldPuns #-}
|
{-# LANGUAGE NamedFieldPuns #-}
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE CPP #-}
|
{-# LANGUAGE CPP #-}
|
||||||
|
@ -9,37 +8,41 @@ module Blog.Skin (
|
||||||
|
|
||||||
import Arguments (Arguments)
|
import Arguments (Arguments)
|
||||||
import qualified Arguments (bannerPath, favicon, cardImage, headPath, previewArticlesCount, previewLinesCount)
|
import qualified Arguments (bannerPath, favicon, cardImage, headPath, previewArticlesCount, previewLinesCount)
|
||||||
|
import Blog.URL (checkURL, pathRelative)
|
||||||
import Control.Monad (filterM)
|
import Control.Monad (filterM)
|
||||||
import Data.Aeson (ToJSON(..), (.=), pairs)
|
import Data.Aeson (ToJSON(..), (.=), object, pairs)
|
||||||
import Data.Maybe (listToMaybe)
|
import Data.Maybe (listToMaybe)
|
||||||
#if !MIN_VERSION_base(4,11,0)
|
#if !MIN_VERSION_base(4,11,0)
|
||||||
import Data.Monoid ((<>))
|
import Data.Monoid ((<>))
|
||||||
#endif
|
#endif
|
||||||
import Files (absoluteLink)
|
import Network.URL (URL)
|
||||||
import GHC.Generics (Generic)
|
|
||||||
import Prelude hiding (head)
|
import Prelude hiding (head)
|
||||||
import System.Directory (doesFileExist)
|
import System.Directory (doesFileExist)
|
||||||
import System.FilePath ((</>), (<.>))
|
import System.FilePath ((</>), (<.>))
|
||||||
|
|
||||||
data Skin = Skin {
|
data Skin = Skin {
|
||||||
banner :: Maybe String
|
banner :: Maybe String
|
||||||
, cardImage :: Maybe FilePath
|
, cardImage :: Maybe URL
|
||||||
, favicon :: Maybe FilePath
|
, favicon :: Maybe URL
|
||||||
, head :: Maybe String
|
, head :: Maybe String
|
||||||
, previewArticlesCount :: Int
|
, previewArticlesCount :: Int
|
||||||
, previewLinesCount :: Int
|
, previewLinesCount :: Int
|
||||||
} deriving Generic
|
}
|
||||||
|
|
||||||
instance ToJSON Skin where
|
instance ToJSON Skin where
|
||||||
|
toJSON (Skin {previewArticlesCount, previewLinesCount}) = object [
|
||||||
|
("previewArticlesCount", toJSON previewArticlesCount)
|
||||||
|
, ("previewLinesCount", toJSON previewLinesCount)
|
||||||
|
]
|
||||||
toEncoding (Skin {previewArticlesCount, previewLinesCount}) = pairs (
|
toEncoding (Skin {previewArticlesCount, previewLinesCount}) = pairs (
|
||||||
"previewArticlesCount" .= previewArticlesCount
|
"previewArticlesCount" .= previewArticlesCount
|
||||||
<> "previewLinesCount" .= previewLinesCount
|
<> "previewLinesCount" .= previewLinesCount
|
||||||
)
|
)
|
||||||
|
|
||||||
findImage :: String -> Maybe FilePath -> IO (Maybe FilePath)
|
findImage :: String -> Maybe FilePath -> IO (Maybe URL)
|
||||||
findImage _ (Just path) = return . Just $ absoluteLink path
|
findImage _ (Just path) = Just <$> checkURL path
|
||||||
findImage name Nothing =
|
findImage name Nothing =
|
||||||
listToMaybe <$> filterM doesFileExist pathsToCheck
|
fmap pathRelative . listToMaybe <$> filterM doesFileExist pathsToCheck
|
||||||
where
|
where
|
||||||
directories = [".", "image", "images", "pictures", "skin", "static"]
|
directories = [".", "image", "images", "pictures", "skin", "static"]
|
||||||
extensions = ["ico", "gif", "jpeg", "jpg", "png", "svg"]
|
extensions = ["ico", "gif", "jpeg", "jpg", "png", "svg"]
|
||||||
|
|
|
@ -1,46 +1,80 @@
|
||||||
{-# LANGUAGE DeriveGeneric #-}
|
|
||||||
{-# LANGUAGE NamedFieldPuns #-}
|
{-# LANGUAGE NamedFieldPuns #-}
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
module Blog.URL (
|
module Blog.URL (
|
||||||
URLs(..)
|
URLs(..)
|
||||||
|
, (./)
|
||||||
|
, (//)
|
||||||
, build
|
, build
|
||||||
|
, checkURL
|
||||||
|
, pathRelative
|
||||||
|
, toText
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Arguments (Arguments)
|
import Arguments (Arguments)
|
||||||
import qualified Arguments as Arguments (Arguments(..))
|
import qualified Arguments as Arguments (Arguments(..))
|
||||||
import Data.Aeson (ToJSON(..), (.=), pairs)
|
import Control.Monad.IO.Class (MonadIO(..))
|
||||||
import GHC.Generics (Generic)
|
import Data.Aeson (ToJSON(..), (.=), object, pairs)
|
||||||
|
import Data.Text (Text, pack)
|
||||||
|
import Network.URL (URL(..), URLType(..), exportURL, importURL)
|
||||||
import System.Exit (die)
|
import System.Exit (die)
|
||||||
import System.IO (hPutStrLn, stderr)
|
import System.IO (hPutStrLn, stderr)
|
||||||
|
import System.FilePath ((</>))
|
||||||
import Text.Printf (printf)
|
import Text.Printf (printf)
|
||||||
|
|
||||||
type URL = String
|
|
||||||
data URLs = URLs {
|
data URLs = URLs {
|
||||||
cards :: Maybe URL
|
cards :: Maybe URL
|
||||||
, comments :: Maybe URL
|
, comments :: Maybe URL
|
||||||
, rss :: Maybe URL
|
, rss :: Maybe URL
|
||||||
} deriving Generic
|
}
|
||||||
|
|
||||||
instance ToJSON URLs where
|
instance ToJSON URLs where
|
||||||
|
toJSON (URLs {comments}) = object [
|
||||||
|
("comments", toJSON (exportURL <$> comments))
|
||||||
|
]
|
||||||
toEncoding (URLs {comments}) = pairs (
|
toEncoding (URLs {comments}) = pairs (
|
||||||
"comments" .= comments
|
"comments" .= (exportURL <$> comments)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
checkURL :: MonadIO m => String -> m URL
|
||||||
|
checkURL url =
|
||||||
|
maybe (liftIO . die $ printf "Invalid URL %s" url) pure $ importURL url
|
||||||
|
|
||||||
|
checksUsed :: Bool -> Maybe a -> IO ()
|
||||||
|
checksUsed False (Just _) =
|
||||||
|
hPutStrLn stderr "Site url is set but not used, did you forget --open-graph-cards or --rss ?"
|
||||||
|
checksUsed _ _ = return ()
|
||||||
|
|
||||||
|
getURL :: Maybe Bool -> Maybe String -> String -> IO (Maybe URL)
|
||||||
|
getURL Nothing (Just url) _ = Just <$> checkURL url
|
||||||
|
getURL (Just True) Nothing reason = die reason
|
||||||
|
getURL (Just True) (Just url) reason = Just <$> checkURL url
|
||||||
|
getURL _ _ _ = pure Nothing
|
||||||
|
|
||||||
build :: Arguments -> IO URLs
|
build :: Arguments -> IO URLs
|
||||||
build arguments = do
|
build arguments = do
|
||||||
cards <- getSiteURL argOGCards "Open Graph cards"
|
cards <- getURL (Just argOGCards) siteURL "Open Graph cards"
|
||||||
rss <- getSiteURL argRSS "RSS feeds"
|
rss <- getURL (Just argRSS) siteURL "RSS feeds"
|
||||||
|
comments <- getURL Nothing commentsURL "Comments"
|
||||||
checksUsed (argOGCards || argRSS) siteURL
|
checksUsed (argOGCards || argRSS) siteURL
|
||||||
return $ URLs {cards, comments, rss}
|
return $ URLs {cards, comments, rss}
|
||||||
where
|
where
|
||||||
comments = Arguments.commentsURL arguments
|
commentsURL = Arguments.commentsURL arguments
|
||||||
siteURL = Arguments.siteURL arguments
|
siteURL = Arguments.siteURL arguments
|
||||||
argOGCards = Arguments.openGraphCards arguments
|
argOGCards = Arguments.openGraphCards arguments
|
||||||
argRSS = Arguments.rss arguments
|
argRSS = Arguments.rss arguments
|
||||||
errorMsg :: String -> String
|
errorMsg :: String -> String
|
||||||
errorMsg = printf "Enabling %s requires setting the site url with --site-url"
|
errorMsg = printf "Enabling %s requires setting the site url with --site-url"
|
||||||
getSiteURL False _ = return Nothing
|
|
||||||
getSiteURL True name = maybe (die $ errorMsg name) (return . Just) siteURL
|
pathRelative :: String -> URL
|
||||||
checksUsed False (Just _) =
|
pathRelative url_path = URL {url_type = PathRelative, url_path, url_params = []}
|
||||||
hPutStrLn stderr "Site url is set but not used, did you forget --open-graph-cards or --rss ?"
|
|
||||||
checksUsed _ _ = return ()
|
toText :: URL -> Text
|
||||||
|
toText = pack . exportURL
|
||||||
|
|
||||||
|
(./) :: URL -> FilePath -> Text
|
||||||
|
(./) url subPath = toText $ url {url_path = url_path url </> subPath}
|
||||||
|
|
||||||
|
(//) :: URL -> URL -> Text
|
||||||
|
(//) _ url@(URL {url_type = Absolute _}) = pack $ exportURL url
|
||||||
|
(//) url (URL {url_type = HostRelative, url_path}) = url ./ ('/':url_path)
|
||||||
|
(//) url (URL {url_path}) = url ./ url_path
|
||||||
|
|
|
@ -11,6 +11,7 @@ import ArticlesList (
|
||||||
ArticlesList(..), description, getArticles, otherURL, rssLinkTexts
|
ArticlesList(..), description, getArticles, otherURL, rssLinkTexts
|
||||||
)
|
)
|
||||||
import Blog (Blog(..), Skin(..), URLs(..), template)
|
import Blog (Blog(..), Skin(..), URLs(..), template)
|
||||||
|
import Blog.URL (toText)
|
||||||
import Control.Monad.Reader (ReaderT, asks)
|
import Control.Monad.Reader (ReaderT, asks)
|
||||||
import Data.Map as Map (Map, toList)
|
import Data.Map as Map (Map, toList)
|
||||||
import Data.Text (Text, pack, empty)
|
import Data.Text (Text, pack, empty)
|
||||||
|
@ -23,6 +24,7 @@ import Lucid (
|
||||||
, title_, toHtml, toHtmlRaw, type_, ul_
|
, title_, toHtml, toHtmlRaw, type_, ul_
|
||||||
)
|
)
|
||||||
import Markdown (Markdown(..), MarkdownContent(..))
|
import Markdown (Markdown(..), MarkdownContent(..))
|
||||||
|
import Network.URL (URL)
|
||||||
import Page (Page)
|
import Page (Page)
|
||||||
import Prelude hiding (head, lookup)
|
import Prelude hiding (head, lookup)
|
||||||
import Pretty ((.$))
|
import Pretty ((.$))
|
||||||
|
@ -83,9 +85,9 @@ defaultBanner =
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
faviconLink :: FilePath -> HtmlGenerator ()
|
faviconLink :: URL -> HtmlGenerator ()
|
||||||
faviconLink url = link_ [
|
faviconLink url = link_ [
|
||||||
rel_ "shortcut icon", href_ . pack $ absoluteLink url, type_ "image/x-icon"
|
rel_ "shortcut icon", href_ $ toText url, type_ "image/x-icon"
|
||||||
]
|
]
|
||||||
|
|
||||||
optional :: (a -> HtmlGenerator ()) -> Maybe a -> HtmlGenerator ()
|
optional :: (a -> HtmlGenerator ()) -> Maybe a -> HtmlGenerator ()
|
||||||
|
|
|
@ -10,6 +10,7 @@ import Article (Article(..))
|
||||||
import ArticlesList (ArticlesList(..))
|
import ArticlesList (ArticlesList(..))
|
||||||
import qualified ArticlesList (description)
|
import qualified ArticlesList (description)
|
||||||
import Blog (Blog(..), Renderer, Skin(..), template)
|
import Blog (Blog(..), Renderer, Skin(..), template)
|
||||||
|
import Blog.URL ((./), (//), checkURL)
|
||||||
import Collection (Collection(..))
|
import Collection (Collection(..))
|
||||||
import qualified Collection (title)
|
import qualified Collection (title)
|
||||||
import Control.Applicative ((<|>))
|
import Control.Applicative ((<|>))
|
||||||
|
@ -19,6 +20,7 @@ import Data.Text (Text, pack)
|
||||||
import Lucid (HtmlT, content_, meta_)
|
import Lucid (HtmlT, content_, meta_)
|
||||||
import Lucid.Base (makeAttribute)
|
import Lucid.Base (makeAttribute)
|
||||||
import Markdown (MarkdownContent(..), metadata)
|
import Markdown (MarkdownContent(..), metadata)
|
||||||
|
import Network.URL (URL)
|
||||||
import qualified Markdown (Markdown(..))
|
import qualified Markdown (Markdown(..))
|
||||||
import Page (Page(..))
|
import Page (Page(..))
|
||||||
import Pretty ((.$))
|
import Pretty ((.$))
|
||||||
|
@ -27,7 +29,7 @@ import System.FilePath.Posix ((</>), (<.>))
|
||||||
class HasCard a where
|
class HasCard a where
|
||||||
cardType :: Renderer m => a -> m Text
|
cardType :: Renderer m => a -> m Text
|
||||||
description :: Renderer m => a -> m Text
|
description :: Renderer m => a -> m Text
|
||||||
image :: Renderer m => a -> m (Maybe String)
|
image :: Renderer m => a -> m (Maybe URL)
|
||||||
title :: Renderer m => a -> m String
|
title :: Renderer m => a -> m String
|
||||||
urlPath :: Renderer m => a -> m String
|
urlPath :: Renderer m => a -> m String
|
||||||
|
|
||||||
|
@ -38,20 +40,19 @@ og attribute value =
|
||||||
, content_ value
|
, content_ value
|
||||||
]
|
]
|
||||||
|
|
||||||
make :: (HasCard a, Renderer m) => a -> String -> HtmlT m ()
|
make :: (HasCard a, Renderer m) => a -> URL -> HtmlT m ()
|
||||||
make element siteURL = do
|
make element siteURL = do
|
||||||
og "url" . sitePrefix =<< urlPath element
|
og "url" . (siteURL ./) =<< urlPath element
|
||||||
og "type" =<< cardType element
|
og "type" =<< cardType element
|
||||||
og "title" . pack =<< title element
|
og "title" . pack =<< title element
|
||||||
og "description" =<< description element
|
og "description" =<< description element
|
||||||
maybeImage =<< ((<|>) <$> image element <*> (asks $skin.$cardImage))
|
maybeImage =<< ((<|>) <$> image element <*> (asks $skin.$cardImage))
|
||||||
og "site_name" =<< (asks $name.$pack)
|
og "site_name" =<< (asks $name.$pack)
|
||||||
where
|
where
|
||||||
maybeImage = maybe (return ()) (og "image" . sitePrefix)
|
maybeImage = maybe (return ()) (og "image" . (siteURL //))
|
||||||
sitePrefix = pack . (siteURL </>)
|
|
||||||
|
|
||||||
mDImage :: (Renderer m, MarkdownContent a ) => a -> m (Maybe String)
|
mDImage :: (Renderer m, MarkdownContent a) => a -> m (Maybe URL)
|
||||||
mDImage = return . Map.lookup "featuredImage" . metadata . getMarkdown
|
mDImage = mapM checkURL . Map.lookup "featuredImage" . metadata . getMarkdown
|
||||||
|
|
||||||
mDTitle :: (Renderer m, MarkdownContent a) => a -> m String
|
mDTitle :: (Renderer m, MarkdownContent a) => a -> m String
|
||||||
mDTitle = return . Markdown.title . getMarkdown
|
mDTitle = return . Markdown.title . getMarkdown
|
||||||
|
|
14
src/RSS.hs
14
src/RSS.hs
|
@ -7,8 +7,9 @@ module RSS (
|
||||||
|
|
||||||
import Article (Article(..))
|
import Article (Article(..))
|
||||||
import ArticlesList (ArticlesList(..), getArticles)
|
import ArticlesList (ArticlesList(..), getArticles)
|
||||||
import qualified ArticlesList (description)
|
import qualified ArticlesList (description, path)
|
||||||
import Blog (Blog(urls), Renderer, URLs(..))
|
import Blog (Blog(urls), Renderer, URLs(..))
|
||||||
|
import Blog.URL ((./))
|
||||||
import Collection (Collection(..), getAll)
|
import Collection (Collection(..), getAll)
|
||||||
import qualified Collection (title)
|
import qualified Collection (title)
|
||||||
import Control.Monad.IO.Class (MonadIO(..))
|
import Control.Monad.IO.Class (MonadIO(..))
|
||||||
|
@ -21,6 +22,7 @@ import Data.Time.Clock.POSIX (posixSecondsToUTCTime)
|
||||||
import Lucid (Attribute, HtmlT, Term, ToHtml(..), term, renderTextT)
|
import Lucid (Attribute, HtmlT, Term, ToHtml(..), term, renderTextT)
|
||||||
import Lucid.Base (makeAttribute)
|
import Lucid.Base (makeAttribute)
|
||||||
import Markdown (Markdown(..))
|
import Markdown (Markdown(..))
|
||||||
|
import Network.URL (URL)
|
||||||
import Pretty ((.$))
|
import Pretty ((.$))
|
||||||
import System.FilePath.Posix ((</>), (<.>))
|
import System.FilePath.Posix ((</>), (<.>))
|
||||||
|
|
||||||
|
@ -57,24 +59,24 @@ item_ = term "item"
|
||||||
pubDate_ :: Term arg result => arg -> result
|
pubDate_ :: Term arg result => arg -> result
|
||||||
pubDate_ = term "pubDate"
|
pubDate_ = term "pubDate"
|
||||||
|
|
||||||
articleItem :: Monad m => String -> Article -> HtmlT m ()
|
articleItem :: Monad m => URL -> Article -> HtmlT m ()
|
||||||
articleItem siteURL (Article (Markdown {path, metadata, title})) =
|
articleItem siteURL (Article (Markdown {path, metadata, title})) =
|
||||||
item_ $ do
|
item_ $ do
|
||||||
title_ $ toHtml title
|
title_ $ toHtml title
|
||||||
link_ $ toHtml (siteURL </> path <.> "html")
|
link_ . toHtml $ siteURL ./ (path <.> "html")
|
||||||
pubDate_ . toHtml . rfc822Date $ metadata ! "date"
|
pubDate_ . toHtml . rfc822Date $ metadata ! "date"
|
||||||
where
|
where
|
||||||
rfc822Date =
|
rfc822Date =
|
||||||
formatTime defaultTimeLocale rfc822DateFormat
|
formatTime defaultTimeLocale rfc822DateFormat
|
||||||
. posixSecondsToUTCTime . fromIntegral . (read :: String -> Int)
|
. posixSecondsToUTCTime . fromIntegral . (read :: String -> Int)
|
||||||
|
|
||||||
feed :: Renderer m => String -> ArticlesList -> HtmlT m ()
|
feed :: Renderer m => URL -> ArticlesList -> HtmlT m ()
|
||||||
feed siteURL al@(ArticlesList {collection}) = do
|
feed siteURL al@(ArticlesList {collection}) = do
|
||||||
prolog
|
prolog
|
||||||
rss_ [version, content, atom] $ do
|
rss_ [version, content, atom] $ do
|
||||||
channel_ $ do
|
channel_ $ do
|
||||||
title_ . toHtml =<< Collection.title collection
|
title_ . toHtml =<< Collection.title collection
|
||||||
link_ . toHtml $ siteURL </> maybe "" (++ "/") (tag collection)
|
link_ . toHtml $ siteURL ./ ArticlesList.path al
|
||||||
description_ . toHtml =<< ArticlesList.description al
|
description_ . toHtml =<< ArticlesList.description al
|
||||||
mapM_ (articleItem siteURL) =<< getArticles al
|
mapM_ (articleItem siteURL) =<< getArticles al
|
||||||
where
|
where
|
||||||
|
@ -82,7 +84,7 @@ feed siteURL al@(ArticlesList {collection}) = do
|
||||||
content = xmlns_content_ "http://purl.org/rss/1.0/modules/content/"
|
content = xmlns_content_ "http://purl.org/rss/1.0/modules/content/"
|
||||||
atom = xmlns_atom_ "http://www.w3.org/2005/Atom"
|
atom = xmlns_atom_ "http://www.w3.org/2005/Atom"
|
||||||
|
|
||||||
generateCollection :: String -> Collection -> ReaderT Blog IO ()
|
generateCollection :: URL -> Collection -> ReaderT Blog IO ()
|
||||||
generateCollection siteURL collection =
|
generateCollection siteURL collection =
|
||||||
renderTextT (feed siteURL $ ArticlesList {full = False, collection})
|
renderTextT (feed siteURL $ ArticlesList {full = False, collection})
|
||||||
>>= liftIO . TextIO.writeFile (basePath collection </> "rss" <.> "xml")
|
>>= liftIO . TextIO.writeFile (basePath collection </> "rss" <.> "xml")
|
||||||
|
|
Loading…
Reference in a new issue