muon 0.1.0.3 → 0.1.0.4
raw patch · 13 files changed
+212/−67 lines, 13 filesdep +happstack-server
Dependencies added: happstack-server
Files
- README +87/−13
- muon.cabal +7/−5
- src/Config.hs +21/−0
- src/Constant.hs +1/−2
- src/Main.hs +11/−5
- src/Page.hs +13/−1
- src/Post.hs +34/−28
- src/Server.hs +7/−0
- src/Site.hs +9/−9
- src/def/config.ini +5/−0
- src/def/posts/first.post +10/−1
- src/def/style/style.css +1/−1
- src/def/templates/header.st +6/−2
README view
@@ -1,11 +1,11 @@ muon ==== Muon is a static blog generator, meaning that it takes files written in-convenient markup and converts it to HTML and CSS ready to deploy to a web-server.+convenient markup and converts it to HTML and CSS ready to deploy to a+web server. -Installing-----------+Installing from Darcs+--------------------- First, install the package by getting the darcs repo. $ darcs get http://repos.kaashif.co.uk/muon@@ -15,9 +15,19 @@ $ cd muon $ cabal install -After that, assuming you have configured cabal and/or your PATH correctly, muon-should be usable. Here are some things you may want to do:+After that, assuming you have configured cabal and/or your PATH+correctly, muon should be usable. Here are some things you may want to+do: +Installing from Hackage+-----------------------+Installing from Hackage using Cabal is as simple as:++ $ cabal install muon++Bear in mind this may not be the most up-to-date version, but it will be+a stable version.+ Using Muon ---------- Initialising a blog:@@ -34,14 +44,78 @@ $ muon generate -Notes------+Previewng the site locally:++ $ muon serve++Uploading site to server:++ $ muon upload++Multiple commands in sequence:++ $ muon init generate upload+ $ muon generate serve++Configuring Muon+----------------+After "muon init" is run, a file called "config.ini" is created. This is+where you configure the blog. By default, it should look like this:++ [site]+ title=Default site+ author=Your Name+ tagline=Something descriptive+ style=/style.css++ [remote]+ user=root+ server=webserver+ dir=/var/www/htdocs/++The [site] section needs little explanation - you can preview the site+yourself and see where those strings go. The "style" option denotes the+location of a custom CSS file.++The [remote] section is to configure the command run by Muon to upload+the site. The command is "rsync -a --delete site/ user@server:dir", with+the key words replaced with your SSH credentials on a server. If you're+not sure if the configuration is correct, remember: the directory must+end in a slash!++Extra Pages+-----------+Aside from a homepage, archive, and posts, you might want some extra+pages on your site, like "example.com/recipes" or "example.com/laptop".+You can add such pages to your site simply by creating a file with the+right name in the "pages/" directory of your blog.++For example, if your blog is at "myblog.com" and you want a page at+"myblog.com/mypage", edit the file "pages/mypage" and fill it with HTML.+Bear in mind, this content will go in between the "header" and "footer"+templates, so you don't need to include <body> or <html> tags. Here's+how you might do that:++ $ cat >> pages/mypage <<EOF+ <h2>My Custom Page</h2>+ <p>This is a page I made!</p>+ EOF++And that's that! Next time you "muon generate upload", the pages will be+accessible at "myblog.com/mypage".++Writing Posts+-------------+To add a new post, create a new file in the "posts/" directory with the+suffix ".post".+ When writing posts, make sure you put the title on the first line, the date on the second, and a short description (for the archive) on the third line. The-rest should be valid Markdown. See the posts/ directory after site+rest should be valid Markdown. See the "posts/" directory after site initialisation for some examples. -The posts are ordered lexicographically, _not_ by date. This means "aaa.post"-will always come before "bbb.post", regardless of the date contained in the file.--"muon upload" is currently hardcoded. Stay tuned for per-blog config files.+In the archive, posts are ordered lexicographically, _not_ by date.+This means "aaa.post" will always come above "bbb.post", regardless of+the date contained in the file itself. This allows you to decide the+order of your posts yourself, if you want to separate tutorials and+rants, for example.
muon.cabal view
@@ -2,16 +2,18 @@ -- see http://haskell.org/cabal/users-guide/ name: muon-version: 0.1.0.3+version: 0.1.0.4 synopsis: Static blog generator description: Program which takes blog posts and pages written in Markdown and compiles them into a tree of HTML pages which can then be served by any web server. .- Muon is similar in function to Hakyll <http://jaspervdj.be/hakyll/>- but has far fewer features (and dependencies) and is generally not- ready for use in production as of yet.+ As of now, Muon supports:+ .+ * Generating a site from Markdown and HTML+ * Previewing a site locally using happstack-server+ * Uploading a site to a server using rsync homepage: http://repos.kaashif.co.uk/darcs?r=muon;a=summary license: BSD3 license-file: LICENSE@@ -36,5 +38,5 @@ executable muon main-is: Main.hs- build-depends: base ==4.6.*, text ==0.11.*, HStringTemplate ==0.7.*, directory ==1.2.*, Glob ==0.7.*, process ==1.2.*, blaze-html ==0.7.*, markdown ==0.1.*, ConfigFile==1.1.*, MissingH==1.2.*+ build-depends: base ==4.6.*, text ==0.11.*, HStringTemplate ==0.7.*, directory ==1.2.*, Glob ==0.7.*, process ==1.2.*, blaze-html ==0.7.*, markdown ==0.1.*, ConfigFile==1.1.*, MissingH==1.2.*, happstack-server==7.3.* hs-source-dirs: src
+ src/Config.hs view
@@ -0,0 +1,21 @@+module Config (getSetting, rsyncCmd) where++import System.Directory+import Data.ConfigFile+import Data.Either.Utils+import qualified Data.Text as T++blogCP :: IO ConfigParser+blogCP = fmap forceEither $ readfile emptyCP "config.ini"++getSetting :: String -> String -> IO String+getSetting sect c = do+ cp <- blogCP+ return $ forceEither $ get cp sect c++rsyncCmd :: IO String+rsyncCmd = do+ user <- getSetting "remote" "user"+ server <- getSetting "remote" "server"+ dir <- getSetting "remote" "dir"+ return $ "rsync -av --delete site/ " ++ user ++ "@" ++ server ++ ":" ++ dir
src/Constant.hs view
@@ -4,6 +4,5 @@ import System.Directory sitePath = "site/"-pageNames = ["about", "contact"] siteDirs = ["site", "style", "posts", "templates", "pages"]-mkdir = createDirectoryIfMissing False+mkdir = createDirectoryIfMissing True
src/Main.hs view
@@ -7,12 +7,14 @@ import System.Directory import System.FilePath.Glob import System.Environment-import System.Process (runCommand)+import System.Process (runInteractiveCommand) import Post (generatePosts) import Home (generateHome) import Archive (generateArchive) import Page (generatePages)+import Server (startServer) import Constant+import Config import Paths_muon ensureDirs = mkdir sitePath@@ -35,7 +37,7 @@ copyStatic = do copyFile "style/style.css" (sitePath ++ "style.css")- runCommand $ "cp -R static " ++ sitePath ++ "static"+ runInteractiveCommand $ "cp -R static " ++ sitePath ++ "static" generateSite = copyStatic >> generatePosts@@ -46,20 +48,24 @@ showHelp = putStrLn "Usage: muon [commands]\n\n\ \ help show this help message\n\ \ init initialise a default blog in the current dir\n\-\ generate generate static blog in 'site' directory\n\n\+\ generate generate static blog in 'site' directory\n\+\ serve serve site at localhost:8000\n\+\ upload upload site to server\n\n\ \Report bugs to <kaashif@kaashif.co.uk>" clearSite = mkdir sitePath >> removeDirectoryRecursive sitePath >> mkdir sitePath -rsyncCmd = "rsync -a --delete site/ root@webserver:/var/www/htdocs/"+printRun :: String -> IO ()+printRun cmd = putStrLn ("Running \'" ++ cmd ++ "\'") >> runInteractiveCommand cmd >> return () proc :: String -> IO () proc cmd | cmd == "init" = createDefaults | cmd == "generate" = clearSite >> generateSite- | cmd == "upload" = void $ runCommand rsyncCmd+ | cmd == "serve" = putStrLn "Serving site at http://127.0.0.1:8000/" >> startServer+ | cmd == "upload" = rsyncCmd >>= printRun | otherwise = showHelp getArgsHelp = do
src/Page.hs view
@@ -6,13 +6,25 @@ import Site import Text.StringTemplate import Text.StringTemplate.GenericStandard+import System.Directory+import Control.Monad import Constant + pageTemplate = do templates <- directoryGroup "templates" :: IO (STGroup T.Text) let Just t = getStringTemplate "generic" templates return t +onlyFiles :: [FilePath] -> IO [FilePath]+onlyFiles fs = filterM doesFileExist fs++pageNames :: IO [String]+pageNames = do+ cs <- getDirectoryContents "pages"+ let fs = map (\x -> "pages/" ++ x) cs+ fmap ( map (drop 6) ) (onlyFiles fs)+ renderPage :: Site -> StringTemplate T.Text -> T.Text -> T.Text renderPage s t c = render $ setAttribute "site" s@@ -35,4 +47,4 @@ writePage p c putStrLn $ "Generated " ++ p -generatePages = mapM_ generatePage pageNames+generatePages = pageNames >>= mapM_ generatePage
src/Post.hs view
@@ -13,12 +13,16 @@ import Data.Typeable import Data.Data import Data.List+import Data.Char+import qualified Data.String.Utils as S import Site import Control.Monad import Constant postPath = "posts/" :: String +replace a b c = S.replace a b (T.unpack c)+ data Post = Post { title :: T.Text , link :: T.Text , date :: T.Text@@ -36,8 +40,11 @@ $ setAttribute "site" s $ setAttribute "post" p t -renderName :: Site -> StringTemplate T.Text -> String -> IO T.Text-renderName s t n = liftM (renderPost s t) (getPost (0, n))+renderPostT :: Post -> IO T.Text+renderPostT p = do+ t <- postTemplate+ s <- readSite+ return $ renderPost s t p makeExtract :: Post -> T.Text makeExtract p = T.pack@@ -48,55 +55,54 @@ ++ "<br/><a href=\'" ++ T.unpack (link p) ++ "\'>Read more...</a>" ++ "<br/><br/><hr/>" -makePost :: (Int, [String]) -> Post-makePost l = Post { title = T.pack $ head (snd l)- , link = T.pack $ "/post/" ++ show (fst l)- , date = T.pack $ snd l !! 1- , comment = T.pack $ snd l !! 2- , content = T.pack $ extractContent (snd l) }+makePost :: [String] -> Post+makePost l = Post { title = T.pack $ head l+ , link = T.pack $ "/" ++ S.replace "-" "/" (l !! 1) ++ "/" ++ linkify (head l)+ , date = T.pack $ l !! 1+ , comment = T.pack $ l !! 2+ , content = T.pack $ extractContent l+ } extractContent :: [String] -> String extractContent l = TL.unpack+ $ TL.replace "<pre>" "<pre class=\'prettyprint\'>" $ renderHtml $ markdown def $ TL.pack $ unlines $ drop 3 l -getPost :: (Int, FilePath) -> IO Post+getPost :: FilePath -> IO Post getPost n = do- c <- readFile (snd n)- return $ makePost (fst n, lines c)+ c <- readFile n+ return $ makePost $ lines c -getNames :: IO [(Int, FilePath)]+getNames :: IO [FilePath] getNames = do d <- globDir [compile "*.post"] postPath- let l = (sort . head . fst) d- return $ zip [1..(length l)] l+ return $ (sort . head . fst) d -renderPosts :: IO [T.Text]-renderPosts = do- templates <- directoryGroup "templates" :: IO (STGroup T.Text)- let Just t = getStringTemplate "post" templates- ns <- getNames- s <- readSite- mapM (renderName s t . snd) ns+linkify :: [Char] -> [Char]+linkify s = filter isLetter $ map toLower $ S.replace " " "-" s -writePosts :: [T.Text] -> IO ()-writePosts ps = mapM_ writePost (zip [1..(length ps)] ps)+writePosts :: [Post] -> IO ()+writePosts ps = mapM_ writePost ps -writePost :: (Int, T.Text) -> IO ()+writePost :: Post -> IO () writePost p = do- mkdir $ sitePath ++ "post"- mkdir $ sitePath ++ "post/" ++ show (fst p)- writeFile (sitePath ++ "post/" ++ show (fst p) ++ "/index.html") (T.unpack $ T.append "\n" $ snd p)+ let d = S.replace "-" "/" (T.unpack $ date p) ++ "/" ++ (linkify $ T.unpack $ title p)+ mkdir $ sitePath ++ d+ c <- renderPostT p+ writeFile (sitePath ++ d ++ "/index.html") (T.unpack $ T.append "\n" c) +getAllPosts :: IO [Post] getAllPosts = do ns <- getNames mapM getPost ns +getAllExtracts :: IO [T.Text] getAllExtracts = do ps <- getAllPosts return $ map makeExtract ps -generatePosts = renderPosts >>= writePosts >> putStrLn "Generated posts"+generatePosts = getAllPosts >>= writePosts >> putStrLn "Generated posts"
+ src/Server.hs view
@@ -0,0 +1,7 @@+module Server where++import Happstack.Server ( Browsing(EnableBrowsing), nullConf+ , serveDirectory, simpleHTTP+ )+startServer :: IO ()+startServer = simpleHTTP nullConf $ serveDirectory EnableBrowsing ["index.html"] "site/"
src/Site.hs view
@@ -7,6 +7,7 @@ import Data.Data import Data.ConfigFile import Data.Either.Utils+import Config data Site = Site { title :: T.Text , style :: T.Text@@ -20,15 +21,14 @@ , tagline = "Description of this blog" } -getSetting :: ConfigParser -> String -> T.Text-getSetting cp s = T.pack $ forceEither $ get cp "site" s- readSite :: IO Site readSite = do- r <- readfile emptyCP "config.ini"- let cp = forceEither r- return $ Site { title = getSetting cp "title"- , style = getSetting cp "style"- , author = getSetting cp "author"- , tagline = getSetting cp "tagline"+ t <- getSetting "site" "title"+ s <- getSetting "site" "style"+ a <- getSetting "site" "author"+ g <- getSetting "site" "tagline"+ return $ Site { title = T.pack t+ , style = T.pack s+ , author = T.pack a+ , tagline = T.pack g }
src/def/config.ini view
@@ -3,3 +3,8 @@ author=Your Name tagline=Something descriptive style=/style.css++[remote]+user=root+server=webserver+dir=/var/www/htdocs/
src/def/posts/first.post view
@@ -2,4 +2,13 @@ 2014-04-05 A comment -Some content+Something interesting. Here is some highlighted code:++ #include <stdio.h>+ + int main()+ {+ printf("Hello, world!\n");+ return 0;+ }+
src/def/style/style.css view
@@ -129,7 +129,7 @@ padding: 20px; background: #DDDDDD; color: #333333;- margin: 0 -20px 20px;+ margin: 0; overflow-x:auto; } li pre {
src/def/templates/header.st view
@@ -3,6 +3,12 @@ <head> <title>$site.title$</title> <link rel="stylesheet" type="text/css" href="$site.style$">+<script type="text/javascript"+ src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML">+</script>+<script + src="https://google-code-prettify.googlecode.com/svn/loader/run_prettify.js?skin=sunburst">+</script> </head> <body> <div class="wrapper">@@ -18,8 +24,6 @@ <li> <a href="/contact">Contact</a></li> <li>//</li> <li> <a href="/archive">Archive</a></li>- <li>//</li>- <li> <a href="http://git.kaashif.co.uk/">Code</a></li> </ul> </header> <hr id="header" />