    {-# LANGUAGE OverloadedStrings #-}
    {-
    Copyright (C) 2009 Gwern Branwen <gwern0@gmail.com> and
    John MacFarlane <jgm@berkeley.edu>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    -}

    -- | Functions for creating Atom feeds for Gitit wikis and pages.

    module Network.Gitit.Feed (FeedConfig(..), filestoreToXmlFeed) where

    import qualified Data.Text as T
    import qualified Data.Text.Lazy as TL
    import Data.Time (UTCTime, formatTime, getCurrentTime, addUTCTime)
    #if MIN_VERSION_time(1,5,0)
    import Data.Time (defaultTimeLocale)
    #else
    import System.Locale (defaultTimeLocale)
    #endif
    import Data.Foldable as F (concatMap)
    import Data.List (intercalate, sortBy, nub)
    import Data.Maybe (fromMaybe)
    import Data.Ord (comparing)
    import Network.URI (isUnescapedInURI, escapeURIString)
    import System.FilePath (dropExtension, takeExtension, (<.>))
    import Data.FileStore.Generic (Diff, PolyDiff(..), diff)
    import Data.FileStore.Types (history, retrieve, Author(authorName), Change(..),
             FileStore, Revision(..), TimeRange(..), RevisionId)
    import Text.Atom.Feed (nullEntry, nullFeed, nullLink, nullPerson,
             Date, Entry(..), Feed(..), Link(linkRel), Generator(..),
             Person(personName), EntryContent(..), TextContent(TextString))
    import Text.Atom.Feed.Export (xmlFeed)
    import Text.XML.Light as XML (showContent, Content(..), Element(..), blank_element, QName(..), blank_name, CData(..), blank_cdata)
    import Text.XML as Text.XML (renderText, Document(..), Element(..),
                                 Prologue(..), def, fromXMLElement)
    import Data.Version (showVersion)
    import Paths_gitit (version)

    data FeedConfig = FeedConfig {
        FeedConfig -> [Char]
    fcTitle    :: String
      , FeedConfig -> [Char]
    fcBaseUrl  :: String
      , FeedConfig -> Integer
    fcFeedDays :: Integer
     } deriving (ReadPrec [FeedConfig]
    ReadPrec FeedConfig
    Int -> ReadS FeedConfig
    ReadS [FeedConfig]
    (Int -> ReadS FeedConfig)
    -> ReadS [FeedConfig]
    -> ReadPrec FeedConfig
    -> ReadPrec [FeedConfig]
    -> Read FeedConfig
    forall a.
    (Int -> ReadS a)
    -> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
    $creadsPrec :: Int -> ReadS FeedConfig
    readsPrec :: Int -> ReadS FeedConfig
    $creadList :: ReadS [FeedConfig]
    readList :: ReadS [FeedConfig]
    $creadPrec :: ReadPrec FeedConfig
    readPrec :: ReadPrec FeedConfig
    $creadListPrec :: ReadPrec [FeedConfig]
    readListPrec :: ReadPrec [FeedConfig]
    Read, Int -> FeedConfig -> ShowS
    [FeedConfig] -> ShowS
    FeedConfig -> [Char]
    (Int -> FeedConfig -> ShowS)
    -> (FeedConfig -> [Char])
    -> ([FeedConfig] -> ShowS)
    -> Show FeedConfig
    forall a.
    (Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
    $cshowsPrec :: Int -> FeedConfig -> ShowS
    showsPrec :: Int -> FeedConfig -> ShowS
    $cshow :: FeedConfig -> [Char]
    show :: FeedConfig -> [Char]
    $cshowList :: [FeedConfig] -> ShowS
    showList :: [FeedConfig] -> ShowS
    Show)

    gititGenerator :: Generator
    gititGenerator :: Generator
    gititGenerator = Generator {genURI :: Maybe NCName
    genURI = NCName -> Maybe NCName
    forall a. a -> Maybe a
    Just NCName
    "http://github.com/jgm/gitit"
                                       , genVersion :: Maybe NCName
    genVersion = NCName -> Maybe NCName
    forall a. a -> Maybe a
    Just ([Char] -> NCName
    T.pack (Version -> [Char]
    showVersion Version
    version))
                                       , genText :: NCName
    genText = NCName
    "gitit"}

    filestoreToXmlFeed :: FeedConfig -> FileStore -> Maybe FilePath -> IO String
    filestoreToXmlFeed :: FeedConfig -> FileStore -> Maybe [Char] -> IO [Char]
    filestoreToXmlFeed FeedConfig
    cfg FileStore
    f = (Feed -> [Char]) -> IO Feed -> IO [Char]
    forall a b. (a -> b) -> IO a -> IO b
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap Feed -> [Char]
    xmlFeedToString (IO Feed -> IO [Char])
    -> (Maybe [Char] -> IO Feed) -> Maybe [Char] -> IO [Char]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . FeedConfig -> Generator -> FileStore -> Maybe [Char] -> IO Feed
    generateFeed FeedConfig
    cfg Generator
    gititGenerator FileStore
    f

    xmlFeedToString :: Feed -> String
    xmlFeedToString :: Feed -> [Char]
    xmlFeedToString Feed
    elt = Text -> [Char]
    TL.unpack (Text -> [Char]) -> (Document -> Text) -> Document -> [Char]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . RenderSettings -> Document -> Text
    renderText RenderSettings
    forall a. Default a => a
    def (Document -> [Char]) -> Document -> [Char]
    forall a b. (a -> b) -> a -> b
    $
      Document{ documentPrologue :: Prologue
    documentPrologue = Prologue{ prologueBefore :: [Miscellaneous]
    prologueBefore = []
                                           , prologueDoctype :: Maybe Doctype
    prologueDoctype = Maybe Doctype
    forall a. Maybe a
    Nothing
                                           , prologueAfter :: [Miscellaneous]
    prologueAfter = [] }
              , documentRoot :: Element
    documentRoot = (Set NCName -> Element)
    -> (Element -> Element) -> Either (Set NCName) Element -> Element
    forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
    either (Element -> Set NCName -> Element
    forall a b. a -> b -> a
    const (Element -> Set NCName -> Element)
    -> Element -> Set NCName -> Element
    forall a b. (a -> b) -> a -> b
    $ Name -> Map Name NCName -> [Node] -> Element
    Text.XML.Element Name
    "feed" Map Name NCName
    forall a. Monoid a => a
    mempty []) Element -> Element
    forall a. a -> a
    id
                                (Either (Set NCName) Element -> Element)
    -> Either (Set NCName) Element -> Element
    forall a b. (a -> b) -> a -> b
    $ Element -> Either (Set NCName) Element
    fromXMLElement (Element -> Either (Set NCName) Element)
    -> Element -> Either (Set NCName) Element
    forall a b. (a -> b) -> a -> b
    $ Feed -> Element
    xmlFeed Feed
    elt
              , documentEpilogue :: [Miscellaneous]
    documentEpilogue = [] }

    generateFeed :: FeedConfig -> Generator -> FileStore -> Maybe FilePath -> IO Feed
    generateFeed :: FeedConfig -> Generator -> FileStore -> Maybe [Char] -> IO Feed
    generateFeed FeedConfig
    cfg Generator
    generator FileStore
    fs Maybe [Char]
    mbPath = do
      UTCTime
    now <- IO UTCTime
    getCurrentTime
      [Revision]
    revs <- Integer -> FileStore -> Maybe [Char] -> UTCTime -> IO [Revision]
    changeLog (FeedConfig -> Integer
    fcFeedDays FeedConfig
    cfg) FileStore
    fs Maybe [Char]
    mbPath UTCTime
    now
      [[([Char], [Diff [[Char]]])]]
    diffs <- (Revision -> IO [([Char], [Diff [[Char]]])])
    -> [Revision] -> IO [[([Char], [Diff [[Char]]])]]
    forall (t :: * -> *) (m :: * -> *) a b.
    (Traversable t, Monad m) =>
    (a -> m b) -> t a -> m (t b)
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
    mapM (FileStore -> Revision -> IO [([Char], [Diff [[Char]]])]
    getDiffs FileStore
    fs) [Revision]
    revs
      let home :: [Char]
    home = FeedConfig -> [Char]
    fcBaseUrl FeedConfig
    cfg [Char] -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    "/"
      -- TODO: 'nub . sort' `persons` - but no Eq or Ord instances!
          persons :: [Person]
    persons = (Author -> Person) -> [Author] -> [Person]
    forall a b. (a -> b) -> [a] -> [b]
    map Author -> Person
    authorToPerson ([Author] -> [Person]) -> [Author] -> [Person]
    forall a b. (a -> b) -> a -> b
    $ [Author] -> [Author]
    forall a. Eq a => [a] -> [a]
    nub ([Author] -> [Author]) -> [Author] -> [Author]
    forall a b. (a -> b) -> a -> b
    $ (Author -> Author -> Ordering) -> [Author] -> [Author]
    forall a. (a -> a -> Ordering) -> [a] -> [a]
    sortBy ((Author -> [Char]) -> Author -> Author -> Ordering
    forall a b. Ord a => (b -> a) -> b -> b -> Ordering
    comparing Author -> [Char]
    authorName) ([Author] -> [Author]) -> [Author] -> [Author]
    forall a b. (a -> b) -> a -> b
    $ (Revision -> Author) -> [Revision] -> [Author]
    forall a b. (a -> b) -> [a] -> [b]
    map Revision -> Author
    revAuthor [Revision]
    revs
          basefeed :: Feed
    basefeed = Generator
    -> [Char] -> [Char] -> Maybe [Char] -> [Person] -> NCName -> Feed
    generateEmptyfeed Generator
    generator (FeedConfig -> [Char]
    fcTitle FeedConfig
    cfg) [Char]
    home Maybe [Char]
    mbPath [Person]
    persons ([Char] -> NCName
    T.pack (UTCTime -> [Char]
    formatFeedTime UTCTime
    now))
          revisions :: [Entry]
    revisions = ((Revision, [([Char], [Diff [[Char]]])]) -> Entry)
    -> [(Revision, [([Char], [Diff [[Char]]])])] -> [Entry]
    forall a b. (a -> b) -> [a] -> [b]
    map ([Char] -> (Revision, [([Char], [Diff [[Char]]])]) -> Entry
    revisionToEntry [Char]
    home) ([Revision]
    -> [[([Char], [Diff [[Char]]])]]
    -> [(Revision, [([Char], [Diff [[Char]]])])]
    forall a b. [a] -> [b] -> [(a, b)]
    zip [Revision]
    revs [[([Char], [Diff [[Char]]])]]
    diffs)
      Feed -> IO Feed
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Feed
    basefeed {feedEntries :: [Entry]
    feedEntries = [Entry]
    revisions}

    -- | Get the last N days history.
    changeLog :: Integer -> FileStore -> Maybe FilePath -> UTCTime -> IO [Revision]
    changeLog :: Integer -> FileStore -> Maybe [Char] -> UTCTime -> IO [Revision]
    changeLog Integer
    days FileStore
    a Maybe [Char]
    mbPath UTCTime
    now' = do
      let files :: [[Char]]
    files = ([Char] -> [[Char]]) -> Maybe [Char] -> [[Char]]
    forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
    F.concatMap (\[Char]
    f -> [[Char]
    f, [Char]
    f [Char] -> ShowS
    <.> [Char]
    "page"]) Maybe [Char]
    mbPath
      let startTime :: UTCTime
    startTime = NominalDiffTime -> UTCTime -> UTCTime
    addUTCTime (Integer -> NominalDiffTime
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (Integer -> NominalDiffTime) -> Integer -> NominalDiffTime
    forall a b. (a -> b) -> a -> b
    $ -Integer
    60 Integer -> Integer -> Integer
    forall a. Num a => a -> a -> a
    * Integer
    60 Integer -> Integer -> Integer
    forall a. Num a => a -> a -> a
    * Integer
    24 Integer -> Integer -> Integer
    forall a. Num a => a -> a -> a
    * Integer
    days) UTCTime
    now'
      [Revision]
    rs <- FileStore -> [[Char]] -> TimeRange -> Maybe Int -> IO [Revision]
    history FileStore
    a [[Char]]
    files TimeRange{timeFrom :: Maybe UTCTime
    timeFrom = UTCTime -> Maybe UTCTime
    forall a. a -> Maybe a
    Just UTCTime
    startTime, timeTo :: Maybe UTCTime
    timeTo = UTCTime -> Maybe UTCTime
    forall a. a -> Maybe a
    Just UTCTime
    now'}
              (Int -> Maybe Int
    forall a. a -> Maybe a
    Just Int
    200) -- hard limit of 200 to conserve resources
      [Revision] -> IO [Revision]
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return ([Revision] -> IO [Revision]) -> [Revision] -> IO [Revision]
    forall a b. (a -> b) -> a -> b
    $ (Revision -> Revision -> Ordering) -> [Revision] -> [Revision]
    forall a. (a -> a -> Ordering) -> [a] -> [a]
    sortBy ((Revision -> Revision -> Ordering)
    -> Revision -> Revision -> Ordering
    forall a b c. (a -> b -> c) -> b -> a -> c
    flip ((Revision -> Revision -> Ordering)
     -> Revision -> Revision -> Ordering)
    -> (Revision -> Revision -> Ordering)
    -> Revision
    -> Revision
    -> Ordering
    forall a b. (a -> b) -> a -> b
    $ (Revision -> UTCTime) -> Revision -> Revision -> Ordering
    forall a b. Ord a => (b -> a) -> b -> b -> Ordering
    comparing Revision -> UTCTime
    revDateTime) [Revision]
    rs

    getDiffs :: FileStore -> Revision -> IO [(FilePath, [Diff [String]])]
    getDiffs :: FileStore -> Revision -> IO [([Char], [Diff [[Char]]])]
    getDiffs FileStore
    fs Revision{ revId :: Revision -> [Char]
    revId = [Char]
    to, revDateTime :: Revision -> UTCTime
    revDateTime = UTCTime
    rd, revChanges :: Revision -> [Change]
    revChanges = [Change]
    rv } = do
      [Revision]
    revPair <- FileStore -> [[Char]] -> TimeRange -> Maybe Int -> IO [Revision]
    history FileStore
    fs [] (Maybe UTCTime -> Maybe UTCTime -> TimeRange
    TimeRange Maybe UTCTime
    forall a. Maybe a
    Nothing (Maybe UTCTime -> TimeRange) -> Maybe UTCTime -> TimeRange
    forall a b. (a -> b) -> a -> b
    $ UTCTime -> Maybe UTCTime
    forall a. a -> Maybe a
    Just UTCTime
    rd) (Int -> Maybe Int
    forall a. a -> Maybe a
    Just Int
    2)
      let from :: Maybe [Char]
    from = if [Revision] -> Int
    forall a. [a] -> Int
    forall (t :: * -> *) a. Foldable t => t a -> Int
    length [Revision]
    revPair Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    >= Int
    2
                    then [Char] -> Maybe [Char]
    forall a. a -> Maybe a
    Just ([Char] -> Maybe [Char]) -> [Char] -> Maybe [Char]
    forall a b. (a -> b) -> a -> b
    $ Revision -> [Char]
    revId (Revision -> [Char]) -> Revision -> [Char]
    forall a b. (a -> b) -> a -> b
    $ [Revision]
    revPair [Revision] -> Int -> Revision
    forall a. HasCallStack => [a] -> Int -> a
    !! Int
    1
                    else Maybe [Char]
    forall a. Maybe a
    Nothing
      [[Diff [[Char]]]]
    diffs <- (Change -> IO [Diff [[Char]]]) -> [Change] -> IO [[Diff [[Char]]]]
    forall (t :: * -> *) (m :: * -> *) a b.
    (Traversable t, Monad m) =>
    (a -> m b) -> t a -> m (t b)
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
    mapM (FileStore
    -> Maybe [Char] -> Maybe [Char] -> Change -> IO [Diff [[Char]]]
    getDiff FileStore
    fs Maybe [Char]
    from ([Char] -> Maybe [Char]
    forall a. a -> Maybe a
    Just [Char]
    to)) [Change]
    rv
      [([Char], [Diff [[Char]]])] -> IO [([Char], [Diff [[Char]]])]
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return ([([Char], [Diff [[Char]]])] -> IO [([Char], [Diff [[Char]]])])
    -> [([Char], [Diff [[Char]]])] -> IO [([Char], [Diff [[Char]]])]
    forall a b. (a -> b) -> a -> b
    $ (([Char], [Diff [[Char]]]) -> ([Char], [Diff [[Char]]]))
    -> [([Char], [Diff [[Char]]])] -> [([Char], [Diff [[Char]]])]
    forall a b. (a -> b) -> [a] -> [b]
    map ([Char], [Diff [[Char]]]) -> ([Char], [Diff [[Char]]])
    forall {a}. ([Char], [a]) -> ([Char], [a])
    filterPages ([([Char], [Diff [[Char]]])] -> [([Char], [Diff [[Char]]])])
    -> [([Char], [Diff [[Char]]])] -> [([Char], [Diff [[Char]]])]
    forall a b. (a -> b) -> a -> b
    $ [[Char]] -> [[Diff [[Char]]]] -> [([Char], [Diff [[Char]]])]
    forall a b. [a] -> [b] -> [(a, b)]
    zip ((Change -> [Char]) -> [Change] -> [[Char]]
    forall a b. (a -> b) -> [a] -> [b]
    map Change -> [Char]
    getFP [Change]
    rv) [[Diff [[Char]]]]
    diffs
      where getFP :: Change -> [Char]
    getFP (Added [Char]
    fp) = [Char]
    fp
            getFP (Modified [Char]
    fp) = [Char]
    fp
            getFP (Deleted [Char]
    fp) = [Char]
    fp
            filterPages :: ([Char], [a]) -> ([Char], [a])
    filterPages ([Char]
    fp, [a]
    d) = case (ShowS
    forall a. [a] -> [a]
    reverse [Char]
    fp) of
                                       Char
    'e':Char
    'g':Char
    'a':Char
    'p':Char
    '.':[Char]
    x -> (ShowS
    forall a. [a] -> [a]
    reverse [Char]
    x, [a]
    d)
                                       [Char]
    _ -> ([Char]
    fp, [])

    getDiff :: FileStore -> Maybe RevisionId -> Maybe RevisionId -> Change -> IO [Diff [String]]
    getDiff :: FileStore
    -> Maybe [Char] -> Maybe [Char] -> Change -> IO [Diff [[Char]]]
    getDiff FileStore
    fs Maybe [Char]
    from Maybe [Char]
    _ (Deleted [Char]
    fp) = do
      [Char]
    contents <- FileStore -> forall a. Contents a => [Char] -> Maybe [Char] -> IO a
    retrieve FileStore
    fs [Char]
    fp Maybe [Char]
    from
      [Diff [[Char]]] -> IO [Diff [[Char]]]
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return [[[Char]] -> Diff [[Char]]
    forall a b. a -> PolyDiff a b
    First ([[Char]] -> Diff [[Char]]) -> [[Char]] -> Diff [[Char]]
    forall a b. (a -> b) -> a -> b
    $ [Char] -> [[Char]]
    lines [Char]
    contents]
    getDiff FileStore
    fs Maybe [Char]
    from Maybe [Char]
    to (Modified [Char]
    fp) = FileStore
    -> [Char] -> Maybe [Char] -> Maybe [Char] -> IO [Diff [[Char]]]
    diff FileStore
    fs [Char]
    fp Maybe [Char]
    from Maybe [Char]
    to
    getDiff FileStore
    fs Maybe [Char]
    _ Maybe [Char]
    to (Added [Char]
    fp) = do
      [Char]
    contents <- FileStore -> forall a. Contents a => [Char] -> Maybe [Char] -> IO a
    retrieve FileStore
    fs [Char]
    fp Maybe [Char]
    to
      [Diff [[Char]]] -> IO [Diff [[Char]]]
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return [[[Char]] -> Diff [[Char]]
    forall a b. b -> PolyDiff a b
    Second ([[Char]] -> Diff [[Char]]) -> [[Char]] -> Diff [[Char]]
    forall a b. (a -> b) -> a -> b
    $ [Char] -> [[Char]]
    lines [Char]
    contents]

    generateEmptyfeed :: Generator -> String ->String ->Maybe String -> [Person] -> Date -> Feed
    generateEmptyfeed :: Generator
    -> [Char] -> [Char] -> Maybe [Char] -> [Person] -> NCName -> Feed
    generateEmptyfeed Generator
    generator [Char]
    title [Char]
    home Maybe [Char]
    mbPath [Person]
    authors NCName
    now =
      Feed
    baseNull {feedAuthors :: [Person]
    feedAuthors = [Person]
    authors,
                feedGenerator :: Maybe Generator
    feedGenerator = Generator -> Maybe Generator
    forall a. a -> Maybe a
    Just Generator
    generator,
                feedLinks :: [Link]
    feedLinks = [ (NCName -> Link
    nullLink (NCName -> Link) -> NCName -> Link
    forall a b. (a -> b) -> a -> b
    $ [Char] -> NCName
    T.pack ([Char] -> NCName) -> [Char] -> NCName
    forall a b. (a -> b) -> a -> b
    $
                                [Char]
    home [Char] -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    "_feed/" [Char] -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ ShowS
    escape ([Char] -> Maybe [Char] -> [Char]
    forall a. a -> Maybe a -> a
    fromMaybe [Char]
    "" Maybe [Char]
    mbPath))
                               {linkRel :: Maybe (Either NCName NCName)
    linkRel = Either NCName NCName -> Maybe (Either NCName NCName)
    forall a. a -> Maybe a
    Just (NCName -> Either NCName NCName
    forall a b. a -> Either a b
    Left NCName
    "self")}]
                }
        where baseNull :: Feed
    baseNull = NCName -> TextContent -> NCName -> Feed
    nullFeed ([Char] -> NCName
    T.pack [Char]
    home) (NCName -> TextContent
    TextString ([Char] -> NCName
    T.pack [Char]
    title)) NCName
    now

    -- Several of the following functions have been given an alternate
    -- version patched in using CPP specifically when the `feed` library
    -- used is version 1.2.x
    --
    -- The 1.2.0.0 release of `feed` introduced an API change that was
    -- reverted in 1.3.0.0.  When feed-1.2.x is sufficiently out of use,
    -- the dependency lower-bound for feed should be updated to >= 1.3 and
    -- the second alternative (#else condition) in each of the CPP
    -- invocations below can be removed.
    --
    -- https://github.com/bergmark/feed/issues/35

    revisionToEntry :: String -> (Revision, [(FilePath, [Diff [String]])]) -> Entry
    revisionToEntry :: [Char] -> (Revision, [([Char], [Diff [[Char]]])]) -> Entry
    revisionToEntry [Char]
    home (Revision{ revId :: Revision -> [Char]
    revId = [Char]
    rid, revDateTime :: Revision -> UTCTime
    revDateTime = UTCTime
    rdt,
                                   revAuthor :: Revision -> Author
    revAuthor = Author
    ra, revDescription :: Revision -> [Char]
    revDescription = [Char]
    rd,
                                   revChanges :: Revision -> [Change]
    revChanges = [Change]
    rv}, [([Char], [Diff [[Char]]])]
    diffs) =
      Entry
    baseEntry{ entryContent :: Maybe EntryContent
    entryContent = EntryContent -> Maybe EntryContent
    forall a. a -> Maybe a
    Just (EntryContent -> Maybe EntryContent)
    -> EntryContent -> Maybe EntryContent
    forall a b. (a -> b) -> a -> b
    $ NCName -> EntryContent
    HTMLContent NCName
    content
               , entryAuthors :: [Person]
    entryAuthors = [Author -> Person
    authorToPerson Author
    ra], entryLinks :: [Link]
    entryLinks = [Link
    ln] }
       where baseEntry :: Entry
    baseEntry = NCName -> TextContent -> NCName -> Entry
    nullEntry ([Char] -> NCName
    T.pack [Char]
    url) TextContent
    title
                              ([Char] -> NCName
    T.pack ([Char] -> NCName) -> [Char] -> NCName
    forall a b. (a -> b) -> a -> b
    $ UTCTime -> [Char]
    formatFeedTime UTCTime
    rdt)
             url :: [Char]
    url = [Char]
    home [Char] -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ ShowS
    escape (Change -> [Char]
    extract (Change -> [Char]) -> Change -> [Char]
    forall a b. (a -> b) -> a -> b
    $ [Change] -> Change
    forall a. HasCallStack => [a] -> a
    head [Change]
    rv) [Char] -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    "?revision=" [Char] -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    rid
             ln :: Link
    ln = (NCName -> Link
    nullLink ([Char] -> NCName
    T.pack [Char]
    url)) {linkRel :: Maybe (Either NCName NCName)
    linkRel = Either NCName NCName -> Maybe (Either NCName NCName)
    forall a. a -> Maybe a
    Just (NCName -> Either NCName NCName
    forall a b. a -> Either a b
    Left NCName
    "alternate")}
             title :: TextContent
    title = NCName -> TextContent
    TextString (NCName -> TextContent) -> NCName -> TextContent
    forall a b. (a -> b) -> a -> b
    $ [Char] -> NCName
    T.pack ([Char] -> NCName) -> [Char] -> NCName
    forall a b. (a -> b) -> a -> b
    $ ((Char -> Bool) -> ShowS
    forall a. (a -> Bool) -> [a] -> [a]
    takeWhile (Char
    '\n' Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    /=) [Char]
    rd) [Char] -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ [Char]
    " - " [Char] -> ShowS
    forall a. [a] -> [a] -> [a]
    ++ ([Char] -> [[Char]] -> [Char]
    forall a. [a] -> [[a]] -> [a]
    intercalate [Char]
    ", " ([[Char]] -> [Char]) -> [[Char]] -> [Char]
    forall a b. (a -> b) -> a -> b
    $ (Change -> [Char]) -> [Change] -> [[Char]]
    forall a b. (a -> b) -> [a] -> [b]
    map Change -> [Char]
    forall a. Show a => a -> [Char]
    show [Change]
    rv)
             df :: [Content]
    df = (([Char], [Diff [[Char]]]) -> Content)
    -> [([Char], [Diff [[Char]]])] -> [Content]
    forall a b. (a -> b) -> [a] -> [b]
    map ([Char], [Diff [[Char]]]) -> Content
    diffFile [([Char], [Diff [[Char]]])]
    diffs
    #if MIN_VERSION_feed(1, 3, 0) || (! MIN_VERSION_feed(1, 2, 0))
             content :: NCName
    content = [Char] -> NCName
    T.pack ([Char] -> NCName) -> [Char] -> NCName
    forall a b. (a -> b) -> a -> b
    $ [[Char]] -> [Char]
    forall (t :: * -> *) a. Foldable t => t [a] -> [a]
    concat ([[Char]] -> [Char]) -> [[Char]] -> [Char]
    forall a b. (a -> b) -> a -> b
    $ (Content -> [Char]) -> [Content] -> [[Char]]
    forall a b. (a -> b) -> [a] -> [b]
    map Content -> [Char]
    showContent [Content]
    df
    #else
             content = XMLTypes.Element (XMLTypes.Name "div" Nothing Nothing) [] df
    #endif

    #if MIN_VERSION_feed(1, 3, 0) || (! MIN_VERSION_feed(1, 2, 0))
    diffFile :: (FilePath, [Diff [String]]) -> Content
    diffFile :: ([Char], [Diff [[Char]]]) -> Content
    diffFile ([Char]
    fp, [Diff [[Char]]]
    d) =
        [Char] -> [Content] -> Content
    enTag [Char]
    "div" ([Content] -> Content) -> [Content] -> Content
    forall a b. (a -> b) -> a -> b
    $ Content
    header Content -> [Content] -> [Content]
    forall a. a -> [a] -> [a]
    : [Content]
    text
      where
        header :: Content
    header = [Char] -> Content -> Content
    enTag1 [Char]
    "h1" (Content -> Content) -> Content -> Content
    forall a b. (a -> b) -> a -> b
    $ [Char] -> Content
    enText [Char]
    fp
        text :: [Content]
    text = (Content -> Content) -> [Content] -> [Content]
    forall a b. (a -> b) -> [a] -> [b]
    map ([Char] -> Content -> Content
    enTag1 [Char]
    "p") ([Content] -> [Content]) -> [Content] -> [Content]
    forall a b. (a -> b) -> a -> b
    $ [[Content]] -> [Content]
    forall (t :: * -> *) a. Foldable t => t [a] -> [a]
    concat ([[Content]] -> [Content]) -> [[Content]] -> [Content]
    forall a b. (a -> b) -> a -> b
    $ (Diff [[Char]] -> [Content]) -> [Diff [[Char]]] -> [[Content]]
    forall a b. (a -> b) -> [a] -> [b]
    map Diff [[Char]] -> [Content]
    diffLines [Diff [[Char]]]
    d
    #else
    diffFile :: (FilePath, [Diff [String]]) -> XMLTypes.Node
    diffFile (fp, d) =
        enTag "div" $ header : text
      where
        header = enTag1 "h1" $ enText $ T.pack fp
        text = map (enTag1 "p") $ concat $ map diffLines d
    #endif

    #if MIN_VERSION_feed(1, 3, 0) || (! MIN_VERSION_feed(1, 2, 0))
    diffLines :: Diff [String] -> [Content]
    diffLines :: Diff [[Char]] -> [Content]
    diffLines (First [[Char]]
    x) = ([Char] -> Content) -> [[Char]] -> [Content]
    forall a b. (a -> b) -> [a] -> [b]
    map ([Char] -> Content -> Content
    enTag1 [Char]
    "s" (Content -> Content) -> ([Char] -> Content) -> [Char] -> Content
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [Char] -> Content
    enText) [[Char]]
    x
    diffLines (Second [[Char]]
    x) = ([Char] -> Content) -> [[Char]] -> [Content]
    forall a b. (a -> b) -> [a] -> [b]
    map ([Char] -> Content -> Content
    enTag1 [Char]
    "b" (Content -> Content) -> ([Char] -> Content) -> [Char] -> Content
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [Char] -> Content
    enText) [[Char]]
    x
    diffLines (Both [[Char]]
    x [[Char]]
    _) = ([Char] -> Content) -> [[Char]] -> [Content]
    forall a b. (a -> b) -> [a] -> [b]
    map [Char] -> Content
    enText [[Char]]
    x
    #else
    diffLines :: Diff [String] -> [XMLTypes.Node]
    diffLines (First x) = map (enTag1 "s" . enText . T.pack) x
    diffLines (Second x) = map (enTag1 "b" . enText . T.pack) x
    diffLines (Both x _) = map (enText . T.pack) x
    #endif

    #if MIN_VERSION_feed(1, 3, 0) || (! MIN_VERSION_feed(1, 2, 0))
    enTag :: String -> [Content] -> Content
    enTag :: [Char] -> [Content] -> Content
    enTag [Char]
    tag [Content]
    content = Element -> Content
    Elem Element
    blank_element{ elName :: QName
    elName=QName
    blank_name{qName :: [Char]
    qName=[Char]
    tag}
                                          , elContent :: [Content]
    elContent=[Content]
    content
                                          }
    #else
    enTag :: T.Text -> [XMLTypes.Node] -> XMLTypes.Node
    enTag tag content = XMLTypes.NodeElement $
      XMLTypes.Element
      (XMLTypes.Name tag Nothing Nothing)
      [] content
    #endif

    #if MIN_VERSION_feed(1, 3, 0) || (! MIN_VERSION_feed(1, 2, 0))
    enTag1 :: String -> Content -> Content
    #else
    enTag1 :: T.Text -> XMLTypes.Node -> XMLTypes.Node
    #endif
    enTag1 :: [Char] -> Content -> Content
    enTag1 [Char]
    tag Content
    content = [Char] -> [Content] -> Content
    enTag [Char]
    tag [Content
    content]

    #if MIN_VERSION_feed(1, 3, 0) || (! MIN_VERSION_feed(1, 2, 0))
    enText :: String -> Content
    enText :: [Char] -> Content
    enText [Char]
    content = CData -> Content
    Text CData
    blank_cdata{cdData :: [Char]
    cdData=[Char]
    content}
    #else
    enText :: T.Text -> XMLTypes.Node
    enText content = XMLTypes.NodeContent (XMLTypes.ContentText content)
    #endif

    -- gitit is set up not to reveal registration emails
    authorToPerson :: Author -> Person
    authorToPerson :: Author -> Person
    authorToPerson Author
    ra = Person
    nullPerson {personName :: NCName
    personName = [Char] -> NCName
    T.pack ([Char] -> NCName) -> [Char] -> NCName
    forall a b. (a -> b) -> a -> b
    $ Author -> [Char]
    authorName Author
    ra}

    -- TODO: replace with Network.URI version of shortcut if it ever is added
    escape :: String -> String
    escape :: ShowS
    escape = (Char -> Bool) -> ShowS
    escapeURIString Char -> Bool
    isUnescapedInURI

    formatFeedTime :: UTCTime -> String
    formatFeedTime :: UTCTime -> [Char]
    formatFeedTime = TimeLocale -> [Char] -> UTCTime -> [Char]
    forall t. FormatTime t => TimeLocale -> [Char] -> t -> [Char]
    formatTime TimeLocale
    defaultTimeLocale [Char]
    "%FT%TZ"

    -- TODO: this boilerplate can be removed by changing Data.FileStore.Types to say
    -- data Change = Modified {extract :: FilePath} | Deleted {extract :: FilePath} | Added
    --                   {extract :: FilePath}
    -- so then it would be just 'escape (extract $ head rv)' without the 4 line definition
    extract :: Change -> FilePath
    extract :: Change -> [Char]
    extract Change
    x = ShowS
    dePage ShowS -> ShowS
    forall a b. (a -> b) -> a -> b
    $ case Change
    x of {Modified [Char]
    n -> [Char]
    n; Deleted [Char]
    n -> [Char]
    n; Added [Char]
    n -> [Char]
    n}
              where dePage :: ShowS
    dePage [Char]
    f = if ShowS
    takeExtension [Char]
    f [Char] -> [Char] -> Bool
    forall a. Eq a => a -> a -> Bool
    == [Char]
    ".page" then ShowS
    dropExtension [Char]
    f else [Char]
    f

    -- TODO: figure out how to create diff links in a non-broken manner
    {-
    diff :: String -> String -> Revision -> Link
    diff home path' Revision{revId = rid} =
                            let n = nullLink (home ++ "_diff/" ++ escape path' ++ "?to=" ++ rid) -- ++ fromrev)
                            in n {linkRel = Just (Left "alternate")}
    -}