muon (empty) → 0.1.0.0
raw patch · 20 files changed
+680/−0 lines, 20 filesdep +Globdep +HStringTemplatedep +basesetup-changed
Dependencies added: Glob, HStringTemplate, base, blaze-html, directory, markdown, process, text
Files
- LICENSE +30/−0
- README +47/−0
- Setup.hs +2/−0
- muon.cabal +32/−0
- src/Archive.hs +45/−0
- src/Constant.hs +9/−0
- src/Home.hs +35/−0
- src/Main.hs +73/−0
- src/Page.hs +37/−0
- src/Post.hs +100/−0
- src/Site.hs +19/−0
- src/def/pages/about +1/−0
- src/def/pages/contact +1/−0
- src/def/posts/first.post +5/−0
- src/def/posts/second.post +5/−0
- src/def/style/style.css +195/−0
- src/def/templates/footer.st +10/−0
- src/def/templates/generic.st +3/−0
- src/def/templates/header.st +26/−0
- src/def/templates/post.st +5/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2014, Kaashif Hymabaccus++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Kaashif Hymabaccus nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README view
@@ -0,0 +1,47 @@+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.++Installing+----------+First, install the package by getting the darcs repo.++ $ darcs get http://repos.kaashif.co.uk/muon++You can install it using cabal, which you should have installed.++ $ 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:++Using Muon+----------+Initialising a blog:++ $ mkdir new-blog+ $ cd new-blog+ $ muon init++Writing a post:++ $ vi posts/new.post++Regenerating the site, creating a tree of files in the ./site directory.++ $ muon generate++Notes+-----+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+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.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ muon.cabal view
@@ -0,0 +1,32 @@+-- Initial muon.cabal generated by cabal init. For further documentation, +-- see http://haskell.org/cabal/users-guide/++name: muon+version: 0.1.0.0+synopsis: Static blog generator+description: Static blog generator+homepage: http://repos.kaashif.co.uk/darcs?r=muon;a=summary+license: BSD3+license-file: LICENSE+author: Kaashif Hymabaccus+maintainer: kaashifhymabaccus@gmail.com+copyright: Copyright (c) 2014 Kaashif Hymabaccus+category: Web+build-type: Simple+cabal-version: >=1.8+data-dir: src+data-files: def/style/style.css+ def/templates/*.st+ def/posts/*.post+ def/pages/about+ def/pages/contact+extra-source-files: src/*.hs README++source-repository head+ type: darcs+ location: http://repos.kaashif.co.uk/muon++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.*+ hs-source-dirs: src
+ src/Archive.hs view
@@ -0,0 +1,45 @@+module Archive where++import System.Directory+import qualified Data.Text as T+import Site+import Text.StringTemplate+import Post++sitePath = "site/"+mkdir = createDirectoryIfMissing False++archiveTemplate = do+ templates <- directoryGroup "templates" :: IO (STGroup T.Text)+ let Just t = getStringTemplate "generic" templates+ return t++makeTableRow :: Post -> String+makeTableRow p = "<tr>"+ ++ "<td><a href=" ++ T.unpack (link p) ++ ">" ++ T.unpack (title p) ++ "</a></td>"+ ++ "<td>" ++ T.unpack (date p) ++ "</td>"+ ++ "<td>" ++ T.unpack (comment p) ++ "</td>"+ ++ "</tr>"++makeTable :: [Post] -> T.Text+makeTable ps = T.pack+ $ "<table>"+ ++ "<tr><td>Title</td><td>Date</td><td>Comment</td></tr>"+ ++ unlines (map makeTableRow ps)+ ++ "</table>"++renderGenericArchive t c = render+ $ setAttribute "site" defaultSite+ $ setAttribute "content" c t++renderArchive = do+ ps <- getAllPosts+ t <- archiveTemplate+ return $ renderGenericArchive t (makeTable $ reverse ps)++writeArchive :: T.Text -> IO ()+writeArchive s = do+ mkdir $ sitePath ++ "archive/"+ writeFile (sitePath ++ "archive/index.html") (T.unpack s)++generateArchive = renderArchive >>= writeArchive >> putStrLn "Generated archive"
+ src/Constant.hs view
@@ -0,0 +1,9 @@+{-# LANGUAGE OverloadedStrings #-}+module Constant where++import System.Directory++sitePath = "site/"+pageNames = ["about", "contact"]+siteDirs = ["site", "style", "posts", "templates", "pages"]+mkdir = createDirectoryIfMissing False
+ src/Home.hs view
@@ -0,0 +1,35 @@+{-# LANGUAGE OverloadedStrings #-}+module Home where++import Post+import Text.StringTemplate+import System.Directory+import Site+import qualified Data.Text as T++sitePath = "site/"+mkdir = createDirectoryIfMissing False++homeTemplate = do+ templates <- directoryGroup "templates" :: IO (STGroup T.Text)+ let Just t = getStringTemplate "generic" templates+ return t++renderGenericHome :: StringTemplate T.Text -> [T.Text] -> T.Text+renderGenericHome t cs = render+ $ setAttribute "site" defaultSite+ $ setAttribute "content" (T.unlines cs) t++renderHome :: IO T.Text+renderHome = do+ cs <- getAllExtracts+ t <- homeTemplate+ return $ renderGenericHome t $ reverse cs++writeHome :: T.Text -> IO ()+writeHome s = do+ mkdir $ sitePath ++ "home/"+ writeFile (sitePath ++ "home/index.html") (T.unpack s)+ writeFile (sitePath ++ "index.html") (T.unpack s)++generateHome = renderHome >>= writeHome >> putStrLn "Generated home"
+ src/Main.hs view
@@ -0,0 +1,73 @@+{-# LANGUAGE OverloadedStrings #-}+module Main where++import qualified Data.Text as T+import Text.StringTemplate+import Control.Monad+import System.Directory+import System.FilePath.Glob+import System.Environment+import System.Process (runCommand)+import Post (generatePosts)+import Home (generateHome)+import Archive (generateArchive)+import Site (defaultSite)+import Page (generatePages)+import Constant+import Paths_muon++ensureDirs = mkdir sitePath++createDefaults = do+ d <- getDataFileName "def"+ mapM_ mkdir siteDirs+ mkdir "static"+ copyFile (d ++ "/style/style.css") "style/style.css"+ copyFile (d ++ "/posts/first.post") "posts/first.post"+ copyFile (d ++ "/posts/second.post") "posts/second.post"+ copyFile (d ++ "/templates/post.st") "templates/post.st"+ copyFile (d ++ "/templates/header.st") "templates/header.st"+ copyFile (d ++ "/templates/footer.st") "templates/footer.st"+ copyFile (d ++ "/templates/generic.st") "templates/generic.st"+ copyFile (d ++ "/pages/about") "pages/about"+ copyFile (d ++ "/pages/contact") "pages/contact"+ putStrLn "Initialised site directory"++copyStatic = do+ copyFile "style/style.css" (sitePath ++ "style.css")+ runCommand $ "cp -R static " ++ sitePath ++ "static"++generateSite = copyStatic+ >> generatePosts+ >> generateHome+ >> generateArchive+ >> generatePages++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\+\Report bugs to <kaashif@kaashif.co.uk>"++clearSite = mkdir sitePath + >> removeDirectoryRecursive sitePath + >> mkdir sitePath++rsyncCmd = "rsync -a --delete site/ root@webserver:/var/www/htdocs/"++proc :: String -> IO ()+proc cmd+ | cmd == "init" = createDefaults+ | cmd == "generate" = clearSite >> generateSite+ | cmd == "upload" = void $ runCommand rsyncCmd+ | otherwise = showHelp++getArgsHelp = do+ a <- getArgs+ if length a < 1+ then return ["help"]+ else return a++takeAction = getArgsHelp >>= mapM_ proc++main = ensureDirs >> takeAction
+ src/Page.hs view
@@ -0,0 +1,37 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE DeriveDataTypeable #-}+module Page where++import qualified Data.Text as T+import Site+import Text.StringTemplate+import Text.StringTemplate.GenericStandard+import Constant++pageTemplate = do+ templates <- directoryGroup "templates" :: IO (STGroup T.Text)+ let Just t = getStringTemplate "generic" templates+ return t++renderPage :: StringTemplate T.Text -> T.Text -> T.Text+renderPage t c = render+ $ setAttribute "site" defaultSite+ $ setAttribute "content" c t++renderPageFile :: String -> IO T.Text+renderPageFile p = do+ c <- readFile $ "pages/" ++ p+ t <- pageTemplate+ return $ renderPage t (T.pack c)++writePage :: String -> T.Text -> IO ()+writePage p c = do+ mkdir $ sitePath ++ p+ writeFile (sitePath ++ p ++ "/index.html") (T.unpack c)++generatePage p = do+ c <- renderPageFile p+ writePage p c+ putStrLn $ "Generated " ++ p++generatePages = mapM_ generatePage pageNames
+ src/Post.hs view
@@ -0,0 +1,100 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE DeriveDataTypeable #-}+module Post where++import qualified Data.Text as T+import qualified Data.Text.Lazy as TL+import Text.StringTemplate+import Text.StringTemplate.GenericStandard+import Text.Blaze.Html.Renderer.Text+import Text.Markdown+import System.Directory+import System.FilePath.Glob+import Data.Typeable+import Data.Data+import Data.List+import Site+import Control.Monad+import Constant++postPath = "posts/" :: String++data Post = Post { title :: T.Text+ , link :: T.Text+ , date :: T.Text+ , comment :: T.Text+ , content :: T.Text+ } deriving (Data, Typeable, Show)++postTemplate = do+ templates <- directoryGroup "templates" :: IO (STGroup T.Text)+ let Just t = getStringTemplate "post" templates+ return t++renderPost :: StringTemplate T.Text -> Post -> T.Text+renderPost t p = render+ $ setAttribute "site" defaultSite+ $ setAttribute "post" p t++renderName :: StringTemplate T.Text -> String -> IO T.Text+renderName t n = liftM (renderPost t) (getPost (0, n))++makeExtract :: Post -> T.Text+makeExtract p = T.pack+ $ "<h2>" ++ T.unpack (title p) ++ "</h2>"+ ++ "<h3>" ++ T.unpack (date p) ++ "</h3>"+ ++ unlines (take 25 $ lines $ T.unpack $ content p)+ ++ "<br/><br/><a href=\'" ++ T.unpack (link p) ++ "\'>Read more...</a>"+ ++ "</code></pre></li></ul></ol><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) }++extractContent :: [String] -> String+extractContent l = TL.unpack+ $ renderHtml+ $ markdown def+ $ TL.pack+ $ unlines+ $ drop 3 l++getPost :: (Int, FilePath) -> IO Post+getPost n = do+ c <- readFile (snd n)+ return $ makePost (fst n, lines c)++getNames :: IO [(Int, FilePath)]+getNames = do+ d <- globDir [compile "*.post"] postPath+ let l = (sort . head . fst) d+ return $ zip [1..(length l)] l++renderPosts :: IO [T.Text]+renderPosts = do+ templates <- directoryGroup "templates" :: IO (STGroup T.Text)+ let Just t = getStringTemplate "post" templates+ ns <- getNames+ mapM (renderName t . snd) ns++writePosts :: [T.Text] -> IO ()+writePosts ps = mapM_ writePost (zip [1..(length ps)] ps)++writePost :: (Int, T.Text) -> 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)++getAllPosts = do+ ns <- getNames+ mapM getPost ns++getAllExtracts = do+ ps <- getAllPosts+ return $ map makeExtract ps++generatePosts = renderPosts >>= writePosts >> putStrLn "Generated posts"
+ src/Site.hs view
@@ -0,0 +1,19 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE DeriveDataTypeable #-}+module Site (defaultSite) where++import qualified Data.Text as T+import Data.Typeable+import Data.Data++data Site = Site { title :: T.Text+ , style :: T.Text+ , author :: T.Text+ , tagline :: T.Text+ } deriving (Data, Typeable, Show)++defaultSite = Site { title = "Default Blog"+ , style = "/style.css"+ , author = "Your Name"+ , tagline = "Description of this blog"+ }
+ src/def/pages/about view
@@ -0,0 +1,1 @@+<p>This is about you.</p>
+ src/def/pages/contact view
@@ -0,0 +1,1 @@+<p>Contact you at your email</p>
+ src/def/posts/first.post view
@@ -0,0 +1,5 @@+First Post+2014-04-05+A comment++Some content
+ src/def/posts/second.post view
@@ -0,0 +1,5 @@+Second Post+2014-04-06+Another comment++Some more content.
+ src/def/style/style.css view
@@ -0,0 +1,195 @@+html {+ background: #FFFFFF;+}++body {+ padding: 50px 0;+ margin: 0;+ font-family: Helvetica;+ color: #555;+ font-weight: 300;+}++.wrapper {+ max-width: 640px;+ margin: 0 auto;+ background: #FFFFFF;+ border-radius: 8px;+}++header, section, footer {+ display: block;+}++a {+ color: #2D2E2F;+ text-decoration: none;+}++p {+ margin: 0 0 20px;+ padding: 0;+}++strong {+ color: #222;+ font-weight: 700;+}++header {+ border-radius: 8px 8px 0 0;+ background: #FFFFFF;+ position: relative;+ padding: 20px 20px 30px 20px;+ margin-top: -1px;+}++header h1 {+ margin: 0;+ padding: 0;+ font-size: 24px;+ line-height: 1.2;+ color: #2D2E2F;+}+header p {+ margin: 0;+ color: #2D2E2F;+ font-size: 15px;+}+header ul {+ float: left;+ list-style: none;+ margin-left: -45px;+}+header ul li {+ float: left;+ padding: 13px 5px;+}+main {+ font-size: 15px;+ border-top: 1px solid #fff;+ position: relative;+ border-radius: 8px;+}++.content {+ padding: 15px 20px 20px 20px;+}++h1, h2, h3, h4, h5, h6 {+ color: #666;+ padding: 0;+ margin: 0 0 20px;+ line-height: 1.2;+}++p, ul, ol, table, pre, dl {+ margin: 0 0 20px;+}++h1, h2, h3 {+ line-height: 1.1;+}++h1 {+ font-size: 28px;+ color: #2D2E2F;+}++h2 {+ color: #2D2E2F;+}++h3, h4, h5, h6 {+ color: #494949;+}++blockquote {+ margin: 0 -20px 20px;+ padding: 15px 20px 1px 40px;+ font-style: italic;+ background: #ccc;+ background: rgba(0, 0, 0, 0.06);+ color: #222;+}++img {+ max-width:100%;+ display: block;+ margin-left: auto;+ margin-right: auto;+}++code, pre {+ color: #333;+ font-size: 12px;+}++pre {+ padding: 20px;+ background: #DDDDDD;+ color: #333333;+ margin: 0 -20px 20px;+ overflow-x:auto;+}+li pre {+ margin-left: -60px;+ padding-left: 60px;+}++table {+ width: 100%;+ border-collapse: collapse;+}++th, td {+ text-align: left;+ padding: 5px 10px;+ border-bottom: 1px solid #aaa;+}++dt {+ color: #222;+ font-weight: 700;+}++th {+ color: #222;+}++small {+ font-size: 11px;+}++hr {+ border: 0;+ background: #aaa;+ height: 0px;+ margin: 0 0 20px;+}++hr#header {+ clear: both;+ visibility: hidden;+}++footer {+ width: 640px;+ margin: 0 auto;+ padding: 20px 0 0;+ color: #aaa;+ overflow: hidden;+}+footer a {+ color: #bbb;+ font-weight: bold;+}+footer p {+ float: left;+}+footer p + p {+ float: right;+}+.latex {+ text-align: center;+}
@@ -0,0 +1,10 @@+</div>+</main>+</div>+<footer>+<p>+© <time>2014</time> $site.author$<br/>+</p>+</footer>+</body>+</html>
+ src/def/templates/generic.st view
@@ -0,0 +1,3 @@+$header()$+$content$+$footer()$
+ src/def/templates/header.st view
@@ -0,0 +1,26 @@+<!DOCTYPE html>+<html>+<head>+<title>$site.title$</title>+<link rel="stylesheet" type="text/css" href="$site.style$">+</head>+<body>+<div class="wrapper">+<main>+<header>+ <h1>$site.title$</h1>+ <p>$site.tagline$</p>+ <ul>+ <li> <a href="/home">Home</a></li>+ <li>//</li>+ <li> <a href="/about">About</a></li>+ <li>//</li>+ <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" />+<div class="content">
+ src/def/templates/post.st view
@@ -0,0 +1,5 @@+$header()$+<h1>$post.title$</h1>+<h2>$post.date$</h2>+$post.content$+$footer()$