packages feed

hakyll-convert 0.1.0.0 → 0.2.0.0

raw patch · 7 files changed

+200/−241 lines, 7 filesdep +containersdep +data-defaultdep −old-localenew-uploaderPVP ok

version bump matches the API change (PVP)

Dependencies added: containers, data-default

Dependencies removed: old-locale

API changes (from Hackage documentation)

- Hakyll.Convert.Blogger: fpComments :: FullPost -> [Entry]
- Hakyll.Convert.Blogger: fpPost :: FullPost -> Entry
- Hakyll.Convert.Blogger: fpUri :: FullPost -> String
- Hakyll.Convert.Blogger: instance Show BloggerEntry
- Hakyll.Convert.Common: dpBody :: DistilledPost -> Text
- Hakyll.Convert.Common: dpCategories :: DistilledPost -> [Text]
- Hakyll.Convert.Common: dpDate :: DistilledPost -> Text
- Hakyll.Convert.Common: dpTags :: DistilledPost -> [Text]
- Hakyll.Convert.Common: dpTitle :: DistilledPost -> Maybe Text
- Hakyll.Convert.Common: dpUri :: DistilledPost -> String
- Hakyll.Convert.Common: instance Data DistilledPost
- Hakyll.Convert.Common: instance Show DistilledPost
- Hakyll.Convert.Common: instance Typeable DistilledPost
+ Hakyll.Convert.Blogger: [fpComments] :: FullPost -> [Entry]
+ Hakyll.Convert.Blogger: [fpPost] :: FullPost -> Entry
+ Hakyll.Convert.Blogger: [fpUri] :: FullPost -> String
+ Hakyll.Convert.Blogger: instance GHC.Show.Show Hakyll.Convert.Blogger.BloggerEntry
+ Hakyll.Convert.Common: [dpBody] :: DistilledPost -> Text
+ Hakyll.Convert.Common: [dpCategories] :: DistilledPost -> [Text]
+ Hakyll.Convert.Common: [dpDate] :: DistilledPost -> UTCTime
+ Hakyll.Convert.Common: [dpTags] :: DistilledPost -> [Text]
+ Hakyll.Convert.Common: [dpTitle] :: DistilledPost -> Maybe Text
+ Hakyll.Convert.Common: [dpUri] :: DistilledPost -> String
+ Hakyll.Convert.Common: instance Data.Data.Data Hakyll.Convert.Common.DistilledPost
+ Hakyll.Convert.Common: instance Data.Default.Class.Default Hakyll.Convert.Common.DistilledPost
+ Hakyll.Convert.OutputFormat: formatPath :: Text -> DistilledPost -> Maybe Text
+ Hakyll.Convert.OutputFormat: validOutputFormat :: Text -> Bool
- Hakyll.Convert.Blogger: distill :: FullPost -> DistilledPost
+ Hakyll.Convert.Blogger: distill :: Bool -> FullPost -> DistilledPost
- Hakyll.Convert.Common: DistilledPost :: String -> Text -> Maybe Text -> [Text] -> [Text] -> Text -> DistilledPost
+ Hakyll.Convert.Common: DistilledPost :: String -> Text -> Maybe Text -> [Text] -> [Text] -> UTCTime -> DistilledPost

Files

Hakyll/Convert/Blogger.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE OverloadedStrings  #-}+{-# LANGUAGE FlexibleContexts   #-} module Hakyll.Convert.Blogger     (FullPost(..), readPosts, distill)   where@@ -20,8 +21,7 @@ import qualified Data.Text                    as T import qualified Data.Text.Encoding           as T import           Data.Time                    (UTCTime)-import           Data.Time.Format             (parseTime, formatTime)-import           System.Locale                (defaultTimeLocale)+import           Data.Time.Format             (parseTimeM, defaultTimeLocale)  import           Hakyll.Core.Compiler import           Hakyll.Core.Item@@ -159,8 +159,8 @@ -- -- --------------------------------------------------------------------- -distill :: FullPost -> DistilledPost-distill fp = DistilledPost+distill :: Bool -> FullPost -> DistilledPost+distill extractComments fp = DistilledPost     { dpBody  = body fpost     , dpUri   = fpUri fp     , dpTitle = title fpost@@ -169,11 +169,37 @@     , dpDate  = date fpost     }   where-    fpost = fpPost fp+    fpost     = fpPost fp+    fcomments = fpComments fp     ---    body = fromContent . entryContent+    body post =+      let article = fromContent $ entryContent post+          comments = T.intercalate "\n" $ map formatComment fcomments+      in if extractComments+           then T.intercalate "\n"+                              [ article+                              , ""+                              , "<h3 id='hakyll-convert-comments-title'>Comments</h3>"+                              , comments]+           else article+     fromContent (Just (HTMLContent x)) = T.pack x     fromContent _ = error "Hakyll.Convert.Blogger.distill expecting HTML"++    formatComment c = T.intercalate "\n" [+        "<div class='hakyll-convert-comment'>"+      , T.concat [ "<p class='hakyll-convert-comment-date'>"+                 , "On ", pubdate, ", ", author, " wrote:"+                 , "</p>" ]+      , "<div class='hakyll-convert-comment-body'>", comment, "</div>"+      , "</div>"+      ]+      where+      pubdate = case entryPublished c of+                    Just d  -> T.pack d+                    Nothing -> "unknown date"+      author = T.unwords $ map (T.pack . personName) (entryAuthors c)+      comment = fromContent $ entryContent c     --     title p = case txtToString (entryTitle p) of          "" -> Nothing@@ -181,16 +207,13 @@     tags = map (T.pack . catTerm)          . filter (not . isBloggerCategory)          . entryCategories-    date x = T.pack $-        case parseTime' =<< entryPublished x of-            Nothing -> "1970-01-01"-            Just  d -> formatTime' d-    parseTime' d = msum $ map (\f -> parseTime defaultTimeLocale f d)+    date x = case parseTime' =<< entryPublished x of+                 Nothing -> fromJust $ parseTime' "1970-01-01T00:00:00Z"+                 Just  d -> d+    parseTime' d = msum $ map (\f -> parseTimeM True defaultTimeLocale f d)         [ "%FT%T%Q%z"  -- with time zone         , "%FT%T%QZ"   -- zulu time         ]-    formatTime' :: UTCTime -> String-    formatTime' = formatTime defaultTimeLocale "%FT%TZ" --for hakyll  -- --------------------------------------------------------------------- -- odds and ends
Hakyll/Convert/Common.hs view
@@ -4,8 +4,12 @@ module Hakyll.Convert.Common where  import           Data.Data+import           Data.Default+import           Data.Maybe import           Data.Text                    (Text) import qualified Data.Text                    as T+import           Data.Time.Clock              (UTCTime)+import           Data.Time.Clock.POSIX        (posixSecondsToUTCTime)  data DistilledPost = DistilledPost     { dpUri   :: String@@ -16,6 +20,16 @@     --   interested in ignoring tags and just focusing on categories     --   in cases where you have lots of little uninteresting tags.     , dpCategories :: [Text]-    , dpDate  :: Text+    , dpDate  :: UTCTime     }-  deriving (Show, Data, Typeable)+  deriving (Data, Typeable)++instance Default DistilledPost where+  def = DistilledPost+    { dpUri        = ""+    , dpBody       = ""+    , dpTitle      = Nothing+    , dpTags       = []+    , dpCategories = []+    , dpDate       = posixSecondsToUTCTime 0+    }
+ Hakyll/Convert/OutputFormat.hs view
@@ -0,0 +1,89 @@+{-# LANGUAGE OverloadedStrings  #-}+{-# LANGUAGE ViewPatterns       #-}+module Hakyll.Convert.OutputFormat+    (validOutputFormat, formatPath)+  where++import           Data.Default+import           Data.Time.Format             (formatTime, defaultTimeLocale)+import           System.FilePath++import           Hakyll.Convert.Common++import qualified Data.Text              as T+import qualified Data.Map.Strict        as M++validOutputFormat :: T.Text -> Bool+validOutputFormat format+  | T.null format = False+  | otherwise =+      case formatPath format def of+        Just _  -> True+        Nothing -> False++formatPath :: T.Text -> DistilledPost -> Maybe T.Text+formatPath format post = helper [] format >>= return.T.concat+  where+  helper acc input =+    case T.uncons input of+      Just ('%', rest) ->+        case T.uncons rest of+          Just (ch,  rest2) ->+            if ch `M.member` acceptableFormats+              then let formatter = acceptableFormats M.! ch+                   in helper ((formatter post):acc) rest2+              else Nothing+      Just (ch,  rest) -> helper ((T.singleton ch):acc) rest+      Nothing          -> Just $ reverse acc++-- When adding a new format, don't forget to update the help message in+-- tools/hakyll-convert.hs+acceptableFormats :: M.Map Char (DistilledPost -> T.Text)+acceptableFormats = M.fromList [+    -- this lets users put literal percent sign in the format)+    ('%', const $ T.singleton '%')++  , ('o', fmtOriginalPath) -- original filepath, like 2016/01/02/blog-post.html+  , ('s', fmtSlug)   -- original slug, i.e. "blog-post" from the example above+  , ('y', fmtYear2)  -- publication year, 2 digits+  , ('Y', fmtYear4)  -- publication year, 4 digits+  , ('m', fmtMonth)  -- publication month+  , ('d', fmtDay)    -- publication day+  , ('H', fmtHour)   -- publication hour+  , ('M', fmtMinute) -- publication minute+  , ('S', fmtSecond) -- publication second+  ]++fmtOriginalPath post =+    T.pack+  . dropTrailingSlash+  . dropExtensions+  $ chopUri (dpUri post)+  where+    dropTrailingSlash = reverse . dropWhile (== '/') . reverse+    chopUri (dropPrefix "http://" -> ("",rest)) =+       -- carelessly assumes we can treat URIs like filepaths+       joinPath $ drop 1 -- drop the domain+                $ splitPath rest+    chopUri u = error $+       "We've wrongly assumed that blog post URIs start with http://, but we got: " ++ u++    dropPrefix :: Eq a => [a] -> [a] -> ([a],[a])+    dropPrefix (x:xs) (y:ys) | x == y    = dropPrefix xs ys+    dropPrefix left right = (left,right)++fmtSlug post =+    T.reverse+  . (T.takeWhile (/= '/'))+  . T.reverse+  $ fmtOriginalPath post++fmtDate format = T.pack . (formatTime defaultTimeLocale format) . dpDate++fmtYear2  = fmtDate "%y"+fmtYear4  = fmtDate "%Y"+fmtMonth  = fmtDate "%m"+fmtDay    = fmtDate "%d"+fmtHour   = fmtDate "%H"+fmtMinute = fmtDate "%M"+fmtSecond = fmtDate "%S"
Hakyll/Convert/Wordpress.hs view
@@ -10,8 +10,7 @@ import qualified Data.Text              as T import qualified Data.Text.Encoding     as T import           Data.Time              (UTCTime)-import           Data.Time.Format       (parseTime, formatTime)-import           System.Locale          (defaultTimeLocale, rfc822DateFormat)+import           Data.Time.Format       (parseTimeM, formatTime, defaultTimeLocale, rfc822DateFormat) import           Text.XML.Light import           Text.RSS.Import import           Text.RSS.Syntax@@ -57,13 +56,11 @@         }     --     date = case parseTime' =<< rssItemPubDate item of-               Nothing -> "1970-01-01"-               Just d  -> T.pack (formatTime' d)-    parseTime' d = msum $ map (\f -> parseTime defaultTimeLocale f d)+               Nothing -> fromJust $ parseTime' "1970-01-01T00:00:00Z"+               Just  d -> d+    parseTime' d = msum $ map (\f -> parseTimeM True defaultTimeLocale f d)         [ rfc822DateFormat         ]-    formatTime' :: UTCTime -> String-    formatTime' = formatTime defaultTimeLocale "%FT%TZ" --for hakyll  -- --------------------------------------------------------------------- -- helpers
hakyll-convert.cabal view
@@ -2,14 +2,18 @@ -- documentation, see http://haskell.org/cabal/users-guide/  name:                hakyll-convert-version:             0.1.0.0+version:             0.2.0.0 synopsis:            Convert from other blog engines to Hakyll.--- description:         -homepage:            http://github.com/kowey/hakyll-convert+description:         WordPress and Blogger only let one export posts in+                     a limited number of formats, none of which are supported+                     by Hakyll. @hakyll-convert@ is created to bridge this gap,+                     providing a way to turn other platform's datadumps into+                     a set of files Hakyll understands.+homepage:            http://github.com/Minoru/hakyll-convert license:             BSD3 license-file:        LICENSE-author:              Eric Kow <eric.kow@gmail.com>-maintainer:          Eric Kow <eric.kow@gmail.com>+author:              Eric Kow <eric.kow@gmail.com>, Alexander Batischev <eual.jp@gmail.com>+maintainer:          Alexander Batischev <eual.jp@gmail.com> -- copyright:            category:            Web build-type:          Simple@@ -19,12 +23,15 @@   exposed-modules:    Hakyll.Convert.Blogger                  ,    Hakyll.Convert.Common                  ,    Hakyll.Convert.Wordpress+                 ,    Hakyll.Convert.OutputFormat   build-depends:       base                ,       binary                ,       bytestring+               ,       containers+               ,       data-default                ,       feed+               ,       filepath                ,       hakyll-               ,       old-locale                ,       text                ,       time                ,       xml@@ -41,13 +48,5 @@                ,       filepath                ,       hakyll-convert                ,       text+               ,       time                ,       xml--executable hakyll-convert-demo-  main-is:             hakyll-convert-demo.hs-  hs-source-dirs:      tools-  -- other-modules:       -  build-depends:       base-               ,       hakyll-               ,       hakyll-convert-               ,       filepath
− tools/hakyll-convert-demo.hs
@@ -1,151 +0,0 @@----------------------------------------------------------------------------------{-# LANGUAGE OverloadedStrings #-}-import           Control.Applicative ((<$>))-import           Data.Monoid         ((<>),mappend,mconcat)-import           Hakyll--import           Hakyll.Convert.Blogger-import           Debug.Trace-----------------------------------------------------------------------------------main :: IO ()-main = hakyll $ do-    match "images/*" $ do-        route   idRoute-        compile copyFileCompiler--    match "css/*" $ do-        route   idRoute-        compile compressCssCompiler--    match (fromList ["about.markdown"]) $ do-        route   $ setExtension "html"-        compile $ pandocCompiler-            >>= loadAndApplyTemplate "templates/default.html" defaultContext-            >>= relativizeUrls--    -- distilled <- match koweycodePattern $ compile bloggerCompiler--    -- tags0 <- buildTags "posts/*" (fromCapture "tags/*.html")-    tags <- buildTags koweycodePattern (fromCapture "tags/*.html")--{--    match "posts/*" $ do-        route $ setExtension "html"-        compile $ pandocCompiler-            >>= loadAndApplyTemplate "templates/post.html"    (postCtx tags)-            >>= loadAndApplyTemplate "templates/default.html" (postCtx tags)-            >>= relativizeUrls--}--    match koweycodePattern $ do-        route $ setExtension "html"-        compile $ pandocCompiler -- TODO shouldn't be needed-            >>= loadAndApplyTemplate "templates/post.html"    (postCtx tags)-            >>= loadAndApplyTemplate "templates/default.html" (postCtx tags)-            >>= relativizeUrls--    -- Post tags-    tagsRules tags $ \tag pattern -> do-        let title = "Posts tagged " ++ tag-            archiveCtx = archiveContext "Posts tagged" tags pattern--        -- Copied from posts, need to refactor-        route idRoute-        compile $ do-            list <- postList tags pattern recentFirst-            makeItem ""-                >>= loadAndApplyTemplate "templates/archive.html" archiveCtx-                >>= loadAndApplyTemplate "templates/default.html" defaultContext-                >>= relativizeUrls--{--        -- Create RSS feed as well-        version "rss" $ do-            route   $ setExtension "xml"-            compile $ loadAllSnapshots pattern "content"-                >>= fmap (take 10) . recentFirst-                >>= renderAtom (feedConfiguration title) feedCtx--}--    let plainPattern = "posts/*"--    create ["archive.html"] $ do-        route idRoute-        compile $ do-            let archiveCtx = archiveContext "Archives" tags koweycodePattern-            makeItem ""-                >>= loadAndApplyTemplate "templates/archive.html" archiveCtx-                >>= loadAndApplyTemplate "templates/default.html" archiveCtx-                >>= relativizeUrls--    match "index.html" $ do-        route idRoute-        compile $ do-            let indexCtx = field "posts" $ \_ -> postList tags koweycodePattern (fmap (take 3) . recentFirst)--            getResourceBody-                >>= applyAsTemplate indexCtx-                >>= loadAndApplyTemplate "templates/default.html" (postCtx tags)-                >>= relativizeUrls--    match "templates/*" $ compile templateCompiler-  where-    koweycodePattern = "koweycode/**"--archiveContext title tags pattern =-    field "posts" (const getList) `mappend`-    constField "title" title      `mappend`-    defaultContext-  where-    getList = postList tags pattern recentFirst------------------------------------------------------------------------------------{--postCtx :: Context String-postCtx =-    dateField "date" "%e %B %Y" `mappend`-    defaultContext--}--postCtx :: Tags -> Context String-postCtx tags = mconcat-    [ modificationTimeField "mtime" "%U"-    , dateField "date" "%e %B %Y"-    , tagsField "tags" tags-    , defaultContext-    ]------------------------------------------------------------------------------------postList :: Tags -> Pattern -> ([Item String] -> Compiler [Item String])-         -> Compiler String-postList tags pattern sortFilter = do-    posts   <- sortFilter =<< loadAll pattern-    itemTpl <- loadBody "templates/post-item.html"-    applyTemplateList itemTpl (postCtx tags) posts--{---postList :: ([Item String] -> [Item String]) -> Compiler String-postList sortFilter = do-    posts1_  <- loadAll "posts/*"-    --posts2_  <- loadAll "koweycode/*/*/*"-    let posts1 = posts1_-        posts2 = []-    let posts = sortFilter (posts1 ++ posts2)-    itemTpl <- loadBody "templates/post-item.html"-    list    <- applyTemplateList itemTpl postCtx posts-    return list---}--{--oldPostList :: ([Item String] -> [Item String]) -> Compiler String-oldPostList sortFilter = do-    posts  <- sortFilter <$> loadAll "koweycode/*/*/*"-    itemTpl <- loadBody "templates/post-item.html"-    list    <- applyTemplateList itemTpl postCtx posts-    return list--}
tools/hakyll-convert.hs view
@@ -1,6 +1,5 @@ {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE OverloadedStrings  #-}-{-# LANGUAGE ViewPatterns       #-}  import           Control.Applicative import           Control.Arrow@@ -14,6 +13,7 @@ import           Data.Monoid import qualified Data.Text              as T import qualified Data.Text.Encoding     as T+import           Data.Time.Format             (formatTime, defaultTimeLocale) import           System.Directory import           System.Environment import           System.FilePath@@ -28,6 +28,7 @@ import           Text.XML.Light  import           Hakyll.Convert.Common+import           Hakyll.Convert.OutputFormat import qualified Hakyll.Convert.Blogger   as Blogger import qualified Hakyll.Convert.Wordpress as Wordpress @@ -35,21 +36,43 @@   deriving (Data, Typeable, Enum, Show)  data Config = Config-    { feed      :: FilePath-    , outputDir :: FilePath-    , format    :: InputFormat+    { feed             :: FilePath+    , outputDir        :: FilePath+    -- underscore will be turned into a dash when generating a commandline+    -- option+    , output_format    :: T.Text+    , format           :: InputFormat+    , extract_comments :: Bool     }  deriving (Show, Data, Typeable)  parameters :: FilePath -> Config parameters p = modes     [ Config-        { feed         = def &= argPos 0 &= typ "ATOM/RSS FILE"-        , outputDir    = def &= argPos 1 &= typDir-        , format       = Blogger &= help "blogger or wordpress"+        { feed             = def &= argPos 0 &= typ "ATOM/RSS FILE"+        , outputDir        = def &= argPos 1 &= typDir+        , output_format    = "%o" &= help outputFormatHelp+        , format           = Blogger &= help "blogger or wordpress"+        , extract_comments = False &= help "Extract comments (Blogger only)"         } &= help "Save blog posts Blogger feed into individual posts"     ] &= program (takeFileName p) +outputFormatHelp = unlines [+    "Output filenames format (without extension)"+  , "Default: %o"+  , "Available formats:"+  , "  %% - literal percent sign"+  , "  %o - original filename (e.g. 2016/01/02/blog-post)"+  , "  %s - original slug (e.g. \"blog-post\")"+  , "  %y - publication year, 2 digits"+  , "  %Y - publication year, 4 digits"+  , "  %m - publication month"+  , "  %d - publication day"+  , "  %H - publication hour"+  , "  %M - publication minute"+  , "  %S - publication second"+  ]+ -- --------------------------------------------------------------------- -- -- ---------------------------------------------------------------------@@ -57,10 +80,15 @@ main = do     p      <- getProgName     config <- cmdArgs (parameters p)-    case format config of-        Blogger   -> mainBlogger   config-        Wordpress -> mainWordPress config +    let ofmt = output_format config++    if not (T.null ofmt || validOutputFormat ofmt)+      then fail $ "Invalid output format string: `" ++ T.unpack (output_format config) ++ "'"+      else case format config of+               Blogger   -> mainBlogger   config+               Wordpress -> mainWordPress config+ mainBlogger :: Config -> IO () mainBlogger config = do     mfeed <- Blogger.readPosts (feed config)@@ -68,7 +96,7 @@         Nothing -> fail $ "Could not understand Atom feed: " ++ feed config         Just fd -> mapM_ process fd   where-    process = savePost config "html" . Blogger.distill+    process = savePost config "html" . Blogger.distill (extract_comments config)  mainWordPress :: Config -> IO () mainWordPress config = do@@ -104,53 +132,13 @@     odir  = outputDir cfg     --     fname    = odir </> postPath <.> ext-    postPath = dropTrailingSlash-             . dropExtensions-             $ chopUri (dpUri post)-      where-        dropTrailingSlash = reverse . dropWhile (== '/') . reverse-        chopUri (dropPrefix "http://" -> ("",rest)) =-           -- carelessly assumes we can treat URIs like filepaths-           joinPath $ drop 1 -- drop the domain-                    $ splitPath rest-        chopUri u = error $-           "We've wrongly assumed that blog post URIs start with http://, but we got: " ++ u+    postPath = T.unpack $ fromJust $ formatPath (output_format cfg) post     --     formatTitle (Just t) = t     formatTitle Nothing  =         "untitled (" <> T.unwords firstFewWords <> "…)"       where         firstFewWords = T.splitOn "-" . T.pack $ takeFileName postPath-    formatDate  = id+    formatDate  = T.pack . formatTime defaultTimeLocale "%FT%TZ" --for hakyll     formatTags  = T.intercalate ","     formatBody  = id--{---- Ugh! convert br tags inside of pre tags-fixupBloggerHtml :: Content -> Content-fixupBloggerHtml = descendElem $ \e ->-    if elName e == unqual "pre"-       then Just . Elem $-                e { elContent = map (descendElem fixBr) (elContent e) }-       else Nothing-  where-    fixBr e =-       if elName e == unqual "br"-          then Just (Text newline)-          else Nothing-    newline = CData CDataRaw "\n" Nothing--descendElem pred (Elem e) =-   case pred e of-       Nothing -> Elem $ e  { elContent = map (descendElem pred) (elContent e) }-       Just e2 -> e2-descendElem _ x = x--}---- ------------------------------------------------------------------------ utilities--- -----------------------------------------------------------------------dropPrefix :: Eq a => [a] -> [a] -> ([a],[a])-dropPrefix (x:xs) (y:ys) | x == y    = dropPrefix xs ys-dropPrefix left right = (left,right)