RssFeed.hs 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. {-# LANGUAGE QuasiQuotes #-}
  2. {-# LANGUAGE RecordWildCards #-}
  3. {-# LANGUAGE OverloadedStrings #-}
  4. {-# LANGUAGE CPP #-}
  5. {-# LANGUAGE GeneralizedNewtypeDeriving #-}
  6. -------------------------------------------------------------------------------
  7. --
  8. -- Module : Yesod.RssFeed
  9. -- Copyright : Patrick Brisbin
  10. -- License : as-is
  11. --
  12. -- Maintainer : Patrick Brisbin <me@pbrisbin.com>
  13. -- Stability : Stable
  14. -- Portability : Portable
  15. --
  16. -------------------------------------------------------------------------------
  17. module Yesod.RssFeed
  18. ( rssFeed
  19. , rssFeedText
  20. , rssLink
  21. , RepRss (..)
  22. , module Yesod.FeedTypes
  23. ) where
  24. import Yesod.Core
  25. import Yesod.FeedTypes
  26. import qualified Data.ByteString.Char8 as S8
  27. import Data.Text (Text, pack)
  28. import Data.Text.Lazy (toStrict)
  29. import Text.XML
  30. import Text.Blaze.Html.Renderer.Text (renderHtml)
  31. import qualified Data.Map as Map
  32. newtype RepRss = RepRss Content
  33. deriving ToContent
  34. instance HasContentType RepRss where
  35. getContentType _ = typeRss
  36. instance ToTypedContent RepRss where
  37. toTypedContent = TypedContent typeRss . toContent
  38. -- | Generate the feed
  39. rssFeed :: MonadHandler m => Feed (Route (HandlerSite m)) -> m RepRss
  40. rssFeed feed = do
  41. render <- getUrlRender
  42. return $ RepRss $ toContent $ renderLBS def $ template feed render
  43. -- | Same as @'rssFeed'@ but for @'Feed Text'@. Useful for cases where you are
  44. -- generating a feed of external links.
  45. rssFeedText :: MonadHandler m => Feed Text -> m RepRss
  46. rssFeedText feed = return $ RepRss $ toContent $ renderLBS def $ template feed id
  47. template :: Feed url -> (url -> Text) -> Document
  48. template Feed {..} render =
  49. Document (Prologue [] Nothing []) root []
  50. where
  51. root = Element "rss" (Map.singleton "version" "2.0") $ return $ NodeElement $ Element "channel" Map.empty $ map NodeElement
  52. $ Element "{http://www.w3.org/2005/Atom}link" (Map.fromList
  53. [ ("href", render feedLinkSelf)
  54. , ("rel", "self")
  55. , ("type", pack $ S8.unpack typeRss)
  56. ]) []
  57. : Element "title" Map.empty [NodeContent feedTitle]
  58. : Element "link" Map.empty [NodeContent $ render feedLinkHome]
  59. : Element "description" Map.empty [NodeContent $ toStrict $ renderHtml feedDescription]
  60. : Element "lastBuildDate" Map.empty [NodeContent $ formatRFC822 feedUpdated]
  61. : Element "language" Map.empty [NodeContent feedLanguage]
  62. : map (flip entryTemplate render) feedEntries
  63. ++
  64. case feedLogo of
  65. Nothing -> []
  66. Just (route, desc) -> [Element "image" Map.empty
  67. [ NodeElement $ Element "url" Map.empty [NodeContent $ render route]
  68. , NodeElement $ Element "title" Map.empty [NodeContent desc]
  69. , NodeElement $ Element "link" Map.empty [NodeContent $ render feedLinkHome]
  70. ]
  71. ]
  72. entryTemplate :: FeedEntry url -> (url -> Text) -> Element
  73. entryTemplate FeedEntry {..} render = Element "item" Map.empty $ map NodeElement $
  74. [ Element "title" Map.empty [NodeContent feedEntryTitle]
  75. , Element "link" Map.empty [NodeContent $ render feedEntryLink]
  76. , Element "guid" Map.empty [NodeContent $ render feedEntryLink]
  77. , Element "pubDate" Map.empty [NodeContent $ formatRFC822 feedEntryUpdated]
  78. , Element "description" Map.empty [NodeContent $ toStrict $ renderHtml feedEntryContent]
  79. ]
  80. ++
  81. case feedEntryEnclosure of
  82. Nothing -> []
  83. Just (EntryEnclosure{..}) -> [
  84. Element "enclosure"
  85. (Map.fromList [("type", enclosedMimeType)
  86. ,("length", pack $ show enclosedSize)
  87. ,("url", render enclosedUrl)]) []]
  88. -- | Generates a link tag in the head of a widget.
  89. rssLink :: MonadWidget m
  90. => Route (HandlerSite m)
  91. -> Text -- ^ title
  92. -> m ()
  93. rssLink r title = toWidgetHead [hamlet|
  94. <link href=@{r} type=#{S8.unpack typeRss} rel="alternate" title=#{title}>
  95. |]