AtomFeed.hs 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. {-# LANGUAGE QuasiQuotes #-}
  2. {-# LANGUAGE RecordWildCards #-}
  3. {-# LANGUAGE OverloadedStrings #-}
  4. {-# LANGUAGE CPP #-}
  5. {-# LANGUAGE GeneralizedNewtypeDeriving #-}
  6. ---------------------------------------------------------
  7. --
  8. -- Module : Yesod.AtomFeed
  9. -- Copyright : Michael Snoyman
  10. -- License : BSD3
  11. --
  12. -- Maintainer : Michael Snoyman <michael@snoyman.com>
  13. -- Stability : Stable
  14. -- Portability : portable
  15. --
  16. -- Generating atom news feeds.
  17. --
  18. ---------------------------------------------------------
  19. -- | Generation of Atom newsfeeds.
  20. module Yesod.AtomFeed
  21. ( atomFeed
  22. , atomFeedText
  23. , atomLink
  24. , RepAtom (..)
  25. , module Yesod.FeedTypes
  26. ) where
  27. import Yesod.Core
  28. import Yesod.FeedTypes
  29. import qualified Data.ByteString.Char8 as S8
  30. import Data.Text (Text)
  31. import Data.Text.Lazy (toStrict)
  32. import Text.XML
  33. import Text.Blaze.Html.Renderer.Text (renderHtml)
  34. import qualified Data.Map as Map
  35. newtype RepAtom = RepAtom Content
  36. deriving ToContent
  37. instance HasContentType RepAtom where
  38. getContentType _ = typeAtom
  39. instance ToTypedContent RepAtom where
  40. toTypedContent = TypedContent typeAtom . toContent
  41. atomFeed :: MonadHandler m => Feed (Route (HandlerSite m)) -> m RepAtom
  42. atomFeed feed = do
  43. render <- getUrlRender
  44. return $ RepAtom $ toContent $ renderLBS def $ template feed render
  45. -- | Same as @'atomFeed'@ but for @'Feed Text'@. Useful for cases where you are
  46. -- generating a feed of external links.
  47. atomFeedText :: MonadHandler m => Feed Text -> m RepAtom
  48. atomFeedText feed = return $ RepAtom $ toContent $ renderLBS def $ template feed id
  49. template :: Feed url -> (url -> Text) -> Document
  50. template Feed {..} render =
  51. Document (Prologue [] Nothing []) (addNS root) []
  52. where
  53. addNS (Element (Name ln _ _) as ns) = Element (Name ln (Just namespace) Nothing) as (map addNS' ns)
  54. addNS' (NodeElement e) = NodeElement $ addNS e
  55. addNS' n = n
  56. namespace = "http://www.w3.org/2005/Atom"
  57. root = Element "feed" Map.empty $ map NodeElement
  58. $ Element "title" Map.empty [NodeContent feedTitle]
  59. : Element "link" (Map.fromList [("rel", "self"), ("href", render feedLinkSelf)]) []
  60. : Element "link" (Map.singleton "href" $ render feedLinkHome) []
  61. : Element "updated" Map.empty [NodeContent $ formatW3 feedUpdated]
  62. : Element "id" Map.empty [NodeContent $ render feedLinkHome]
  63. : Element "author" Map.empty [NodeElement $ Element "name" Map.empty [NodeContent feedAuthor]]
  64. : map (flip entryTemplate render) feedEntries
  65. ++
  66. case feedLogo of
  67. Nothing -> []
  68. Just (route, _) -> [Element "logo" Map.empty [NodeContent $ render route]]
  69. entryTemplate :: FeedEntry url -> (url -> Text) -> Element
  70. entryTemplate FeedEntry {..} render = Element "entry" Map.empty $ map NodeElement $
  71. [ Element "id" Map.empty [NodeContent $ render feedEntryLink]
  72. , Element "link" (Map.singleton "href" $ render feedEntryLink) []
  73. , Element "updated" Map.empty [NodeContent $ formatW3 feedEntryUpdated]
  74. , Element "title" Map.empty [NodeContent feedEntryTitle]
  75. , Element "content" (Map.singleton "type" "html") [NodeContent $ toStrict $ renderHtml feedEntryContent]
  76. ]
  77. ++
  78. case feedEntryEnclosure of
  79. Nothing -> []
  80. Just (EntryEnclosure{..}) ->
  81. [Element "link" (Map.fromList [("rel", "enclosure")
  82. ,("href", render enclosedUrl)]) []]
  83. -- | Generates a link tag in the head of a widget.
  84. atomLink :: MonadWidget m
  85. => Route (HandlerSite m)
  86. -> Text -- ^ title
  87. -> m ()
  88. atomLink r title = toWidgetHead [hamlet|
  89. <link href=@{r} type=#{S8.unpack typeAtom} rel="alternate" title=#{title}>
  90. |]