module WWWStaticusPlugins ( staticusPluginsDefault ,staticusPluginTimestamp ,staticusPluginInit ,staticusPluginCfg ,staticusPluginCopy ,staticusPluginPandoc ,staticusPluginFill ,staticusPluginWrite ,staticusPluginCards ,staticusDownloadEmbed ) where import WWWStaticus import System.Process import System.Directory as D import System.IO.Error import System.FilePath import Data.Time.Clock as C import Data.Time.Calendar import qualified Data.Map.Strict as M import qualified Data.Text as T import Control.Monad import Data.Char import Data.Maybe import Data.Either import Data.List import Data.List.Split import Data.List.Utils import Data.Tuple.Extra import Text.Pandoc import Text.Regex import Network.HTTP.Simple import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Lazy.Char8 as Char8 -- IDEAS -- * download file from inet and save -- * tags -- * search function -- * generate sitemap -- * pdf output -- * asciinema autorecorder -- * links checker -- * multiple templates -- * contact form (via nginx log?) -- * multilang / autotranslate -- |little helper to get values out of a map easily -- error if not found. par k m = case M.lookup k m of Just x -> x Nothing -> error $ "key not found: "++k -- |little helper to get values out of a map easily -- defaulting to empty string. par' k m = case M.lookup k m of Just x -> x Nothing -> "" -- |A default set of plugins that you can use to kickstart your page -- Use now, Adapt later. staticusPluginsDefault::[StaticusPlugin] staticusPluginsDefault=[ staticusPluginInit ,staticusPluginTimestamp ,staticusPluginCfg ,staticusPluginCopy [".jpg",".png",".zip",".css",".js",".ico",".mp4",".ogv",".hs",".glade",".cast",".pdf",".html"] "640x480" ,staticusPluginCards ,staticusDownloadEmbed ,staticusPluginPandoc ,staticusPluginFill ,staticusPluginWrite ] -- |Check if result is younger than source index.md, otherwise set "abort" staticusPluginTimestamp::StaticusPlugin staticusPluginTimestamp = StaticusPlugin "init" id runIO where runIO m = do t0 <- getT $ par "path" m t1 <- getT $ par "path" m++"/"++"index.md" t2 <- getT $ par "outdir" m++"/"++"index.html" let abort = if(max t0 t1"
  • =1 && brc!!0==x then " active" else "")++"\">"++x++"
  • ") (menu t) let submenu= concat $ map (\x->"
  • "++cln x++"
  • ") (menu c) let breadcrumbs=concat $ map (\(x,d)->"
  • "++(if d==0 then "" else "")++cln x++(if d==0 then "" else "")++"
  • ") (zip ("home":brc) [brcl,brcl-1..]) mdex <- doesFileExist $ par "path" m++"/"++"index.md" md <- if mdex then readFile $ par "path" m++"/"++"index.md" else return "" tmpl <- readFile $ par "dir_in" m++"/"++"template.html" return $ M.union (M.fromList [ ("outdir",outdir) ,("index.md",md) ,("template.html",tmpl) ,("menutop",menutop) ,("submenu",submenu) ,("breadcrumbs",if isHome then "" else breadcrumbs) ,("title",if null brc then "Home" else cln $ last brc) ]) m where outdir = par "dir_out" m ++ "/" ++ intercalate "/" brc htmlroot_dir = htmlroot ++ "/" ++ intercalate "/" brc htmlroot = par "html_root" m brc = remUnder $ splitOn "/" (drop (length (par "dir_in" m)) (par "path" m)) brcl = length brc remUnder = filter (not.null) . map (dropWhile(=='_').dropWhile(/='_')) menu = remUnder . filter (not . isPrefixOf "00_") . map (last.splitOn "/") . sortBy (flip compare) isHome = par "path" m==par "dir_in" m cln x = replace "-" " " x -- |This plugin will: -- -- 1) copy files of given extension from source to the target directory. -- 2) create scaled versions of all .png and .jpg files to fit in the given dims. (using convert) -- 3) create mp4 version of ogv/ogg files. (using ffmpeg) -- -- Example Params [".png",".zip",".css"] "640x480" -- staticusPluginCopy::[String]->String->StaticusPlugin staticusPluginCopy ext dims = StaticusPlugin "Copy and More" id runIO where runIO m = do dl <- listDirectory (par "path" m) mapM_ (work dl m) [(copyFile,ext), (scaleFile, [".jpg",".png"]), (videoFile,[".ogv",".ogg"])] return m work dl m (f,exts) = mapM_ (\x->f (par "path" m++"/"++x) (par "outdir" m++"/"++x)) $ extFilter exts dl extFilter ext = filter f where f x=or $ map ($x) (map isSuffixOf ext) scaleFile = systemExe renImg "convert" "" ("-resize "++dims) videoFile = systemExe (flip (-<.>) ".mp4") "ffmpeg" "-i" "" renImg = uncurry replaceFileName . second (("scale_"++).takeFileName) . dupe systemExe renOut cmd fl1 fl2 f1 f2 = void $ system $ intercalate " " [cmd,fl1,qt f1,fl2,qt $ renOut f2] qt = ('\"':).(++"\"") -- |extracts config lines (###>>>) from 'index.md' staticusPluginCfg::StaticusPlugin staticusPluginCfg = StaticusPlugin "extract config lines" run return where run m = M.union (M.fromList (("index.md",indexmd):conf)) m where indexmd=unlines . filter (not.isPrefixOf "###>>>") . lines $ par "index.md" m conf= map (first head.second (unwords.tail).dupe.words) . filter (isPrefixOf "###>>>") . lines $ par "index.md" m -- |pipes 'index.md' value through pandoc and saves in 'content' staticusPluginPandoc::StaticusPlugin staticusPluginPandoc = StaticusPlugin "pandoc" run return where run m = let f = runPure $ do doc <- readMarkdown def{readerExtensions=(pandocExtensions`mappend`(extensionsFromList [Ext_emoji,Ext_raw_html]) )} $ T.pack (par "index.md" m) writeHtml5String def doc in M.union (M.fromList [("content",T.unpack (fromRight (T.pack "err") f))]) m -- |Plugin for filling the 'template.html' value with 'content', 'menu', -- etc and storing in 'final' staticusPluginFill::StaticusPlugin staticusPluginFill = StaticusPlugin "fill template" run return where run m = M.union (M.fromList [("final", replace "###BREADCRUMBS###" (par "breadcrumbs" m) $ replace "###SUBMENU###" (par "submenu" m) $ replace "###MENU###" (par "menutop" m) $ replace "###CONTENT###" (par "content" m) $ replace "###ROOT###" (par "html_root" m) $ replace "###TITLE###" (t (par' "###>>>TITLE" m,par "title" m)) $ replace "###DESCRIPTION###" (par' "###>>>DSC" m) $ replace "###KEYWORDS###" (par' "###>>>KWD" m) $ (par "template.html" m))]) m t (a,b) = if a=="" then b else a -- |write final index.html file staticusPluginWrite::StaticusPlugin staticusPluginWrite = StaticusPlugin "writer" id runIO where runIO m = do writeFile ((par "outdir" m)++"/index.html") (par "final" m) return m -- |Todo: abstract some chucker for this plugin and others! -- |Embed data fetched via http/https staticusDownloadEmbed::StaticusPlugin staticusDownloadEmbed=StaticusPlugin "download and embed" id runIO where runIO m = do m'<-makeEmbed $ par "index.md" m return $ M.union (M.fromList[("index.md",concat m')]) m makeEmbed x = evr2 $ splitRegex (mkRegex "^\\{BEGIN:EMBED\\}.*$|^\\{END:EMBED\\}.*$") x evr2 (x:y:xs) = do doc<-httpLBS $ parseRequest_ (filter (not.isControl) y) rest<-evr2 xs return $ x:(Char8.unpack $ getResponseBody doc):rest evr2 x = return x -- |Todo: some generalized snipmate instead -- |simple bootstrap cards staticusPluginCards::StaticusPlugin staticusPluginCards = StaticusPlugin "bootstrap cards" run return where run m = M.union (M.fromList[("index.md",makeCards $ par "index.md" m)]) m makeCards x = concat $ evr2 $ splitRegex (mkRegex "^\\{BEGIN:CARD\\}.*$|^\\{END:CARD\\}.*$") x evr2 (x:y:xs) = x:createCard y:evr2 xs evr2 x = x createCard v = let (_:x:y:z:_:xs)=lines v in replace "###IMG###" x $ replace "###TITLE###" y $ replace "###FOOTER###" z -- (z++"
    "++a) $ replace "###TEXT###" (unlines $ map spans xs) tmpl tmpl ="
    " ++"
    " ++"" ++"
    " ++"
    ###TITLE###
    " ++"

    " ++"###TEXT###" ++"

    " ++"
    " ++"" ++"
    " spans ('-':vv)=let (x:xs)=words vv in ""++unwords xs++"" spans xx = xx