panda 0.0.0.2 → 0.0.0.3
raw patch · 28 files changed
+478/−272 lines, 28 filesdep +HaXmldep +haskell98
Dependencies added: HaXml, haskell98
Files
- LICENSE +11/−18
- Panda/Config/Global.hs +2/−1
- Panda/Controller/Application.hs +38/−15
- Panda/Helper/Env.hs +0/−5
- Panda/Helper/Escape.hs +22/−0
- Panda/Helper/Helper.hs +35/−23
- Panda/Model/Post.hs +5/−4
- Panda/Model/Tag.hs +13/−32
- Panda/Type/State.hs +1/−2
- Panda/View/RSS.hs +1/−3
- Panda/View/Theme/BluePrint/Body.hs +0/−60
- Panda/View/Theme/BluePrint/Footer.hs +0/−21
- Panda/View/Theme/BluePrint/Header.hs +0/−16
- Panda/View/Theme/BluePrint/Helper.hs +13/−15
- Panda/View/Theme/BluePrint/Post.hs +4/−3
- Panda/View/Theme/BluePrint/Search.hs +16/−0
- Panda/View/Theme/BluePrint/Sidebar.hs +0/−34
- Panda/View/Theme/BluePrint/Static.hs +1/−1
- Panda/View/Theme/BluePrint/Tag.hs +1/−1
- Panda/View/Theme/BluePrint/Template.hs +0/−11
- Panda/View/Theme/BluePrint/Template/Body.hs +61/−0
- Panda/View/Theme/BluePrint/Template/Footer.hs +21/−0
- Panda/View/Theme/BluePrint/Template/Header.hs +18/−0
- Panda/View/Theme/BluePrint/Template/Sidebar.hs +41/−0
- Panda/View/Theme/BluePrint/Template/Template.hs +11/−0
- changelog.markdown +25/−0
- panda.cabal +15/−7
- readme.markdown +123/−0
LICENSE view
@@ -1,20 +1,13 @@-Copyright (c) 2008 Wang, Jinjing--Permission is hereby granted, free of charge, to any person obtaining a copy-of this software and associated documentation files (the "Software"), to deal-in the Software without restriction, including without limitation the rights-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell-copies of the Software, and to permit persons to whom the Software is-furnished to do so, subject to the following conditions:--The above copyright notice and this permission notice shall be included in-all copies or substantial portions of the Software.+(c) 2008 Wang, Jinjing -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENMPS. IN NO EVENT SHALL THE-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN-THE SOFTWARE.+This work ‘as-is’ we provide.+No warranty, express or implied.+We’ve done our best,+to debug and test.+Liability for damages denied. +Permission is granted hereby,+to copy, share, and modify.+Use as is fit,+free or for profit.+On this notice these rights rely.
Panda/Config/Global.hs view
@@ -2,7 +2,6 @@ import Panda.Helper.Env import Prelude hiding ((.), (/), id)-import MPS db_id = "db" flat_id = "."@@ -32,3 +31,5 @@ author_email = config_for "author_email" panda_url = "http://github.com/nfjinjing/panda"++per_page = 7 :: Int
Panda/Controller/Application.hs view
@@ -1,29 +1,27 @@ {-# OPTIONS -fno-monomorphism-restriction #-} -module Panda.Controller.Application (pages) where-+module Panda.Controller.Application where import qualified Data.Map as Map -- env import Panda.Helper.Env import Prelude hiding ((.), (/), id)-import Kibro (kibro) -import Kibro.DB.Sqlite3 import Panda.Type.State hiding (pager) import qualified Panda.Config.Global as Config import qualified Panda.Type.Pager as Pager -- model-import Panda.Model.Post as Post-import Panda.Model.Tag as Tag+import qualified Panda.Model.Post as Post+import qualified Panda.Model.Tag as Tag -- view import qualified Panda.View.Theme.BluePrint.Post as PostV-import qualified Panda.View.Theme.BluePrint.Template as T+import qualified Panda.View.Theme.BluePrint.Template.Template as T import qualified Panda.View.Theme.BluePrint.Static as StaticV import qualified Panda.View.Theme.BluePrint.Tag as TagV-import qualified Panda.View.RSS as RSS+import qualified Panda.View.Theme.BluePrint.Search as SearchV+import qualified Panda.View.RSS as RSSV @@ -32,26 +30,30 @@ pages = [ ("^/$" ,index ) , ("^/([?].+$)?$" ,index )- , ("^/rss.xml$" ,blog_feed )+ , ("^/rss.xml$" ,index_feed ) , (blog_regex ,blog ) , ("^/static/." ,static )+ , ("^/tag/.*/rss.xml$" ,tag_feed ) , ("^/tag/." ,tag )- , ("^/tag/.*([?].+$)?$" ,index )+ , ("^/search" ,search ) ] +per_page = Config.per_page+ index = do blogs <- Post.list.liftIO- p <- pager 10 $ blogs.length+ p <- pager per_page $ blogs.length tags <- Tag.list.liftIO let nav = if p.Pager.current == 1 then Home else Other let state = State Config.blog_id p tags nav blogs.map (Tag.fill_tag tags) .PostV.list state .output_html -blog_feed = do+index_feed = do blogs <- Post.list.liftIO- blogs.RSS.rss.output+ blogs.RSSV.rss.output + blog = do id <- uri <.> ("blog/" ++) blog <- Post.get id .liftIO@@ -71,11 +73,32 @@ tag = do id <- uri tags <- Tag.list.liftIO- let tag_name = Tag.name_from_id id+ let tag_name = Tag.id_to_name id case Tag.tag_map' tags .Map.lookup tag_name of Nothing -> "page not found" .output Just blog_set -> do blogs <- blog_set.to_list.rsort.mapM (Post.get) .liftIO <.> map (Tag.fill_tag tags)- p <- pager 10 $ blogs.length+ p <- pager per_page $ blogs.length let state = State id p tags Other blogs.TagV.view state.output_html++tag_feed = do+ id <- uri <.> (split "/" >>> init >>> join "/")+ tags <- Tag.list.liftIO+ let tag_name = Tag.id_to_name id+ case Tag.tag_map' tags .Map.lookup tag_name of+ Nothing -> "page not found" .output+ Just blog_set -> do+ blogs <- blog_set.to_list.rsort.mapM (Post.get) .liftIO <.> map (Tag.fill_tag tags)+ blogs.RSSV.rss.output++search = do+ s <- param_with_default "s" ""+ blogs <- Post.search s .liftIO++ p <- pager per_page $ blogs.length+ tags <- Tag.list.liftIO+ query <- uri+ let state = State query p tags Other++ blogs.map (Tag.fill_tag tags) .SearchV.view state s.output_html
Panda/Helper/Env.hs view
@@ -1,6 +1,5 @@ module Panda.Helper.Env ( module MPS - , module System.Directory , module Control.Monad , module Control.Arrow , module Data.List @@ -14,16 +13,12 @@ ) where import MPS-import System.Directory-import Kibro hiding (tag)-import Kibro.DB.Sqlite3 import Control.Monad hiding (join) import Control.Arrow ((>>>)) import Data.List import Network.URI import Network.CGI import Text.Pandoc.UTF8-import Text.XHtml.Strict import System.Locale import System.Time.Parse import Panda.Helper.Helper
+ Panda/Helper/Escape.hs view
@@ -0,0 +1,22 @@+module Panda.Helper.Escape where+++import Text.XML.HaXml hiding ((!))+import Text.XML.HaXml.Parse (xmlParseWith, document)+import Text.XML.HaXml.Lex (xmlLex)++++unescape :: String -> String+unescape = concatMap ctext . xmlUnEscapeContent stdXmlEscaper .+ unwrapTag .+ either error id . fst . xmlParseWith document .+ xmlLex "oops, lexer failed" . wrapWithTag "t"+ where+ ctext (CString _ txt _) = txt+ ctext (CRef (RefEntity name) _) = '&' : name ++ ";" -- skipped by escaper+ ctext (CRef (RefChar num) _) = '&' : '#' : show num ++ ";" -- ditto+ ctext _ = error "oops, can't unescape non-cdata"+ wrapWithTag t s = concat ["<", t, ">", s, "</", t, ">"]+ unwrapTag (Document _ _ (Elem _ _ c) _) = c+ unwrapTag _ = error "oops, not wrapped"
Panda/Helper/Helper.hs view
@@ -8,35 +8,43 @@ import Control.Arrow ((>>>), (&&&)) import Text.XHtml.Strict import Control.Monad hiding (join)+import Data.Maybe -import Panda.Type.Pager as Pager+import Panda.Type.Pager as Pager hiding (empty) import MPS import Prelude hiding ((.), (/), id)+import Panda.Helper.Escape+import Char+import Data.List+import System.Directory (/) = (</>) + -- controller-uri = do- uri <- requestURI- uri.uriPath.urlDecode.tail.return+uri = requestURI <.> (uriPath >>> urlDecode >>> tail) -markdown_link l s = ["[", s, "]", "(", l, ")"] .join'- params = do query_string <- requestURI <.> uriQuery case query_string of- '?':s -> s.split "&" .map (split "=" >>> tuple2) .return+ '?':s -> s.formDecode .return otherwise -> return [] param_with_default s d = do ps <- params return $ case ps.lookup s of Nothing -> d- Just x -> x.read+ Just x -> x.unescape.strip +input_with_default s d = do+ input <- getInput s+ return $ case input of+ Nothing -> d+ Just x -> x.unescape.strip+ pager length total = liftM5 ( Pager length ) current has_next has_previous next previous where- current = param_with_default "page" 1+ current = param_with_default "page" "1" <.> read has_next = current <.> ( (* length) >>> (< total.fromIntegral) ) has_previous = current <.> (> 1) next = current <.> (+ 1)@@ -46,27 +54,21 @@ for_current_page p xs = xs.drop ((p.Pager.current - 1) * p.Pager.length) .take (p.Pager.length) -- -- view-id = identifier+id = identifier -css_link l = itag "link" ! [rel "stylesheet", thetype "text/css", href l]-js_link l = itag "script" ! [thetype "text/javascript", src l]-svg s a w h = image ! [src ("/svg/" ++ s), height (h.show), width (w.show), alt a]+css_link l = itag "link" ! [rel "stylesheet", thetype "text/css", href l]+js_link l = itag "script" ! [thetype "text/javascript", src l]+rss_link l = itag "link" ! [rel "alternate", thetype "application/rss+xml", href l, title "RSS 2.0"]+meta_tag = meta ! [httpequiv "Content-Type", content "text/html; charset=utf-8"] -meta_tag = meta ! [httpequiv "Content-Type", content "text/html; charset=utf-8"] -div_id s = thediv ! [id s]-div_class s = thediv ! [theclass s]+div_id s = thediv ! [id s]+div_class s = thediv ! [theclass s] div_class_id x y = thediv ! [theclass x, id y]-space = hr ! [theclass "space"] -sep_by _ [] = toHtml ""-sep_by _ [x] = toHtmlFromList [x]-sep_by x xs = ( init >>> map (+++ x) ) &&& last >>> splash (+++) $ xs+output_html = output ... renderHtml -output_html = output ... renderHtml ie s = [ "<!-- [if IE]>"@@ -81,3 +83,13 @@ uriQuery = "", uriFragment = "" }++++-- generic+lower = map toLower+upper = map toUpper++ls s = getDirectoryContents s <.> (\\ [".", ".."])++filter_comment = lines >>> map strip >>> reject null >>> reject (head >>> (== '#')) >>> join "\n"
Panda/Model/Post.hs view
@@ -3,7 +3,7 @@ module Panda.Model.Post where -- env-import Panda.Helper.Env+import Panda.Helper.Env hiding (match) import Prelude hiding ((.), (/), id) import qualified Panda.Config.Global as Config @@ -15,9 +15,7 @@ } deriving (Show, Eq) -list = do- ids <- getDirectoryContents Config.blog_uri <.> (\\ [".", ".."]) <.> rsort <.> map ("" / Config.blog_id /)- mapM get ids+list = ls Config.blog_uri <.> rsort <.> map (Config.blog_id /) >>= mapM get get id = liftM2 (Post id (get_title id) ) (get_body id) (return []) get_title id = id.fromUTF8.words.tail.join " "@@ -28,3 +26,6 @@ Nothing -> parse_date "2000-1-1".fromJust Just x -> x +match s x = [title, body] .map (send_to x >>> lower >>> isInfixOf (s.lower)) .or+search "" = return []+search s = list <.> filter (match s)
Panda/Model/Tag.hs view
@@ -19,40 +19,21 @@ } deriving (Show, Eq) -name x = x.uid.name_from_id-name_from_id x = x.split "/" .tail.join'-sorted xs = xs.List.sortBy(\a b -> (b.resources.Set.size) `compare` (a.resources.Set.size))-id_from_name x = tag_id / x--list = do- ids <- getDirectoryContents Config.tag_uri <.> (\\ [".", ".."]) <.> map ("" / Config.tag_id /)- mapM get ids--get id = get_resources id <.> Tag id-get_resources id = (Config.flat_uri / id) .readFile - <.> lines <.> map strip <.> reject empty <.> reject (head >>> (== '#')) <.> map (blog_id / ) <.> to_set+name x = x.uid.id_to_name+id_to_name x = x.split "/" .tail.join'+sorted xs = xs.List.sortBy(\a b -> (b.resources.Set.size) `compare` (a.resources.Set.size))+name_to_id x = tag_id / x +list = ls Config.tag_uri <.> map (Config.tag_id /) >>= mapM get +get id = get_resources id <.> Tag id+get_resources id = (Config.flat_uri / id) .readFile + <.> filter_comment <.> lines <.> map (blog_id / ) <.> to_set --- many to many relationship is modeled using 2 maps--- one from tags, and the other from resources--to_tuple x = (x.name, x.resources)--tag_map' xs = xs . map (to_tuple) . to_h-tag_map = list <.> tag_map'--tag_reverse_map' xs = res.labeling (for_resource xs) where- res = xs.map resources .reduce Set.union .to_list--tag_reverse_map = list <.> tag_reverse_map'--uncategorized = []-for_resource xs x = case tags of - [] -> uncategorized- otherwise -> tags- where- tags = xs.select (resources >>> Set.member x) .map name+to_tuple x = (x.name, x.resources)+tag_map' xs = xs . map (to_tuple) . to_h+tag_map = list <.> tag_map' -fill_tag xs x = x { Post.tags = for_resource xs (x.Post.uid) }+for_resource xs x = xs.select (resources >>> Set.member x) .map name+fill_tag xs x = x { Post.tags = for_resource xs (x.Post.uid) }
Panda/Type/State.hs view
@@ -13,5 +13,4 @@ , nav_location :: NavLocation } -data NavLocation = Home | About | Other- deriving (Eq, Show)+data NavLocation = Home | About | Other deriving (Eq, Show)
Panda/View/RSS.hs view
@@ -14,9 +14,7 @@ entry_uri x = "/" ++ Post.uid x.urlEncode.split "/".tail.join "/" -- RSS-channel_rss_template = - [ Language "en-us"- ]+channel_rss_template = [ Language "en-us" ] item_rss_template x = [ Title $ x.Post.title
− Panda/View/Theme/BluePrint/Body.hs
@@ -1,60 +0,0 @@-module Panda.View.Theme.BluePrint.Body where---- env-import Panda.Helper.Env-import Prelude hiding ((.), (/), id, span)-import Panda.Config.Global as Config-import qualified Panda.Type.State as State-import Text.XHtml.Strict---- view-import Panda.View.Theme.BluePrint.Sidebar-import Panda.View.Theme.BluePrint.Footer---html_body state x = body << container << - [ body_header state- , div_id "page" << [ body_content x, html_sidebar state ]- , html_footer- ]--container = div_class "container"--body_header state = toHtml- [ div_class_id "column span-12 first" "header" << - [ search_form- , site_name- ]- , div_class_id "column span-12 first large" "nav" << navigation state- ]--search_form = spaceHtml-search_form' = - div_id "search" << gui "/search" << thediv << - textfield "s" ! [strAttr "onFocus" "clearInput('s', 'Search')", strAttr "onBlur" "clearInput('s', 'Search')"]- -site_name = toHtml $- [ toHtml $ hotlink "/" ! [theclass "logo"] << ""- , h1 << Config.blog_title- , div_class "description" << Config.blog_subtitle- ]--navigation state = div_class "content" <<- ulist << - [ li ! [theclass ("first" ++ current_home)] << home_link- , li ! [theclass ("page_item page-item-2" ++ current_about)] << about- ]- where- nav = state.State.nav_location- current = " current_page_item"- current_home = at State.Home- current_about = at State.About- at x = if nav == x then current else ""--home_link = hotlink "/" << "Home"-about = hotlink "/static/about" << "About"---body_content x = div_class_id "column span-9 first" "maincontent" <<- div_class "content" << x-
@@ -1,21 +0,0 @@-module Panda.View.Theme.BluePrint.Footer where- -import Panda.Helper.Env-import Prelude hiding ((.), (/), id, span)-import Panda.Config.Global as Config-import qualified Panda.Type.State as State-import Text.XHtml.Strict---import Panda.Config.Global as Config--html_footer = div_id "footer" <<- [ toHtml $ copyright- , toHtml $ "2008 "- , toHtml $ Config.blog_title- , toHtml $ br- , toHtml $ "Powered by "- , toHtml $ hotlink Config.panda_url << "Panda"- , toHtml $ " using "- , toHtml $ hotlink "http://www.haskell.org/" << "Haskell"- ]
− Panda/View/Theme/BluePrint/Header.hs
@@ -1,16 +0,0 @@-module Panda.View.Theme.BluePrint.Header where--import Panda.Helper.Env-import Prelude hiding ((.), (/), id, span)-import Panda.Config.Global as Config-import qualified Panda.Type.State as State-import Text.XHtml.Strict---html_head = header << [meta_tag, title_tag, screen_css, wp_css] -- , ie_css, custom_css]--title_tag = thetitle << Config.blog_title-screen_css = css_link "/theme/blueprint/wp/css/screen.css"-wp_css = css_link "/theme/blueprint/wp/css/blueprint-wp.css"--- ie_css = ie $ css_link "theme/blueprint/core/ie.css"-custom_css = css_link "/theme/blueprint/wp/css/custom.css"
Panda/View/Theme/BluePrint/Helper.hs view
@@ -1,34 +1,32 @@ module Panda.View.Theme.BluePrint.Helper where -import Kibro -import MPS-import Prelude hiding ((.), (/), id)-import Control.Arrow ((>>>)) -import Panda.Helper.Helper-import Panda.View.Theme.BluePrint.Template-import Panda.Model.Post as Post+import Panda.Helper.Env+import Prelude hiding ((.), (/), id, span) import Panda.Config.Global as Config-import Panda.Type.Pager as Pager-import Network.URI-import Text.RSS-import System.Time-import System.Locale import qualified Panda.Type.State as State+import Text.XHtml.Strict+ +import Panda.View.Theme.BluePrint.Template.Template+import Panda.Model.Post as Post+import Panda.Type.Pager as Pager + nav p r = [ div_class "alighleft" << nav_next p r , div_class "alignright" << nav_previous p r ] nav_previous p r = if p.Pager.has_previous - then toHtml $ hotlink ( r ++ "?page=" ++ p.Pager.previous.show ) << previous_sign+ then toHtml $ hotlink ( r' ++ "page=" ++ p.Pager.previous.show ) << previous_sign else spaceHtml-+ where r' = if isSuffixOf "&" r then r else r ++ "?"+ nav_next p r = if p.Pager.has_next- then toHtml $ hotlink ( r ++ "?page=" ++ p.Pager.next.show ) << next_sign+ then toHtml $ hotlink ( r' ++ "page=" ++ p.Pager.next.show ) << next_sign else spaceHtml+ where r' = if isSuffixOf "&" r then r else r ++ "?" next_sign = "Next Entries »" previous_sign = "« Previous Entries"
Panda/View/Theme/BluePrint/Post.hs view
@@ -17,13 +17,13 @@ -- view import Panda.View.Theme.BluePrint.Helper-import Panda.View.Theme.BluePrint.Template+import Panda.View.Theme.BluePrint.Template.Template -- HTML -- render single entry entry x = div_class "post" << [entry_title, entry_mark, entry_body] where entry_title = x.title_link- entry_body = p ! [theclass "entry"] << x.Post.body.markup+ entry_body = div_class "entry" << x.Post.body.markup entry_mark = p ! [theclass "small"] << [ x.blog_date, x.blog_tags ] @@ -34,7 +34,8 @@ blog_tags x | x.tags.null = toHtml "" blog_tags x | otherwise = toHtml " | " +++ toHtml ["Published in ".toHtml, x.tags.map tag_link .intersperse (", ".toHtml) .toHtml] -tag_link s = toHtml $ hotlink ("/tag/" ++ s ) << s+tag_link s = toHtml $ hotlink ("/tag/" ++ s ) << s+ -- entry view view state x = x.entry.page state
+ Panda/View/Theme/BluePrint/Search.hs view
@@ -0,0 +1,16 @@+module Panda.View.Theme.BluePrint.Search where+ +import Panda.Helper.Env+import Prelude hiding ((.), (/), id, span)+import Panda.Config.Global as Config+import qualified Panda.Type.State as State+import Text.XHtml.Strict++-- view+import Panda.View.Theme.BluePrint.Post hiding (view)+import Panda.View.Theme.BluePrint.Helper+import Panda.View.Theme.BluePrint.Template.Template++view state s = for_current_page p >>> map entry >>> (+++ nav p ("/search" ++ "?s=" ++ (s.urlEncode) ++ "&")) >>> page state+ where p = state.State.pager+
− Panda/View/Theme/BluePrint/Sidebar.hs
@@ -1,34 +0,0 @@-module Panda.View.Theme.BluePrint.Sidebar where--import Panda.Helper.Env-import Prelude hiding ((.), (/), id, span)-import Panda.Config.Global as Config-import qualified Panda.Type.State as State-import Text.XHtml.Strict--import qualified Data.Set as Set---- model-import Panda.Model.Tag as Tag--html_sidebar state = - div_class_id "column span-3 last" "sidebar" << unordList- [ feed- , [hr]- , tag_list $ state.State.tags- ]--feed = - [ h2 << "Keep Update"- , p ! [theclass "small"] << rss_link- ]--rss_link = hotlink "/rss.xml" << - [ -- image ! [src "/image/feed.png", alt "feed link", height "15", width "15"]- "Subscribe to Feed" ]- -tag_list tags = - [ h2 << "Tags"- , unordList $ tags.Tag.sorted.map tag_link]- -tag_link x = hotlink ("/" ++ x.uid) << x.Tag.name +++ (" (" ++ x.Tag.resources.Set.size.show ++ ")")
Panda/View/Theme/BluePrint/Static.hs view
@@ -6,6 +6,6 @@ import qualified Panda.Type.State as State import Text.XHtml.Strict -import Panda.View.Theme.BluePrint.Template+import Panda.View.Theme.BluePrint.Template.Template view tags x = x.markup.page tags
Panda/View/Theme/BluePrint/Tag.hs view
@@ -12,7 +12,7 @@ -- view import Panda.View.Theme.BluePrint.Post hiding (view) import Panda.View.Theme.BluePrint.Helper-import Panda.View.Theme.BluePrint.Template+import Panda.View.Theme.BluePrint.Template.Template view state = for_current_page p >>> map entry >>> (+++ nav p ("/" ++ tag_id)) >>> page state where p = state.State.pager
− Panda/View/Theme/BluePrint/Template.hs
@@ -1,11 +0,0 @@-module Panda.View.Theme.BluePrint.Template where--import Panda.View.Theme.BluePrint.Header-import Panda.View.Theme.BluePrint.Body--import Panda.Helper.Env--page state x = - [ html_head- , html_body state x- ]
+ Panda/View/Theme/BluePrint/Template/Body.hs view
@@ -0,0 +1,61 @@+module Panda.View.Theme.BluePrint.Template.Body where++-- env+import Panda.Helper.Env+import Prelude hiding ((.), (/), id, span)+import Panda.Config.Global as Config+import qualified Panda.Type.State as State+import Text.XHtml.Strict++-- view+import Panda.View.Theme.BluePrint.Template.Sidebar+import Panda.View.Theme.BluePrint.Template.Footer+++html_body state x = body << container << + [ body_header state+ , div_id "page" << [ body_content x, html_sidebar state ]+ , html_footer+ ]++container = div_class "container"++body_header state = toHtml+ [ div_class_id "column span-12 first" "header" << + [ search_form+ , site_name+ ]+ , div_class_id "column span-12 first large" "nav" << navigation state+ ]++-- search_form = spaceHtml+search_form = + div_id "search" << form ! [action "/search", method "get"] << thediv << + textfield "s" -- ! [strAttr "onFocus" "clearInput('s', 'Search')", strAttr "onBlur" "clearInput('s', 'Search')"]+ +site_name = toHtml $+ [ toHtml $ hotlink "/" ! [theclass "logo"] << ""+ , h1 << Config.blog_title+ , div_class "description" << Config.blog_subtitle+ ]++navigation state = div_class "content" <<+ ulist << + [ li ! [theclass ("first" ++ current_home)] << home_link+ , li ! [theclass ("page_item page-item-2" ++ current_about)] << about+ ]+ where+ nav = state.State.nav_location+ current = " current_page_item"+ current_home = at State.Home+ current_about = at State.About+ at x = if nav == x then current else ""+++home_link = hotlink "/" << "Home"+about = hotlink "/static/about" << "About"+++body_content x = div_class_id "column span-9 first" "maincontent" <<+ div_class "content" << x+
@@ -0,0 +1,21 @@+module Panda.View.Theme.BluePrint.Template.Footer where+ +import Panda.Helper.Env+import Prelude hiding ((.), (/), id, span)+import Panda.Config.Global as Config+import qualified Panda.Type.State as State+import Text.XHtml.Strict+++import Panda.Config.Global as Config++html_footer = div_id "footer" <<+ [ toHtml $ copyright+ , toHtml $ "2008 "+ , toHtml $ Config.blog_title+ , toHtml $ br+ , toHtml $ "Powered by "+ , toHtml $ hotlink Config.panda_url << "Panda"+ , toHtml $ " using "+ , toHtml $ hotlink "http://www.haskell.org/" << "Haskell"+ ]
+ Panda/View/Theme/BluePrint/Template/Header.hs view
@@ -0,0 +1,18 @@+module Panda.View.Theme.BluePrint.Template.Header where++import Panda.Helper.Env+import Prelude hiding ((.), (/), id, span)+import Panda.Config.Global as Config+import qualified Panda.Type.State as State+import Text.XHtml.Strict+++html_head = header << [meta_tag, title_tag, screen_css, wp_css, custom_css, rss_tag] -- , ie_css, custom_css]++title_tag = thetitle << Config.blog_title+screen_css = css_link "/theme/blueprint/wp/css/screen.css"+wp_css = css_link "/theme/blueprint/wp/css/blueprint-wp.css"+-- ie_css = ie $ css_link "theme/blueprint/core/ie.css"+custom_css = css_link "/theme/blueprint/wp/css/custom.css"++rss_tag = rss_link "rss.xml"
+ Panda/View/Theme/BluePrint/Template/Sidebar.hs view
@@ -0,0 +1,41 @@+module Panda.View.Theme.BluePrint.Template.Sidebar where++import Panda.Helper.Env hiding (rss_link)+import Prelude hiding ((.), (/), id, span)+import Panda.Config.Global as Config+import qualified Panda.Type.State as State+import Text.XHtml.Strict++import qualified Data.Set as Set++-- model+import Panda.Model.Tag as Tag++html_sidebar state = + div_class_id "column span-3 last" "sidebar" << unordList+ [ feed state+ , hr+ , tag_list $ state.State.tags+ ]++feed state = toHtml+ [ h2 << "Subscribe"+ , p ! [theclass "feed"] << rss_link state+ ]++rss_link state = + if tagged + then+ let tag_name = uid.split "/".tail.first in+ link ("tag" / tag_name) tag_name+ else link "" "Home"+ where+ uid = State.uid state+ tagged = uid.match "^tag/.+" .isJust+ link r s = hotlink ("/" / r / "rss.xml") ! [ theclass "feedlink" ] << s++tag_list tags = toHtml+ [ h2 << "Tags"+ , unordList $ tags.Tag.sorted.map tag_link]+ +tag_link x = hotlink ("/" ++ x.uid) << x.Tag.name +++ (" (" ++ x.Tag.resources.Set.size.show ++ ")")
+ Panda/View/Theme/BluePrint/Template/Template.hs view
@@ -0,0 +1,11 @@+module Panda.View.Theme.BluePrint.Template.Template where++import Panda.View.Theme.BluePrint.Template.Header+import Panda.View.Theme.BluePrint.Template.Body++import Panda.Helper.Env++page state x = + [ html_head+ , html_body state x+ ]
+ changelog.markdown view
@@ -0,0 +1,25 @@+0.0.0.3+--------++### New Features++* Search+* Feed per tag+* Theme through CSS++### Migration++* Checkout `public/theme/blueprint/wp/css/custom.css` in `panda-template`++0.0.0.2+--------++### General++* A new blueprint theme adopted from <http://www.fireandknowledge.org/archives/2007/09/05/blueprint-wordpress-theme/>+* Support for tags, see `panda-template` for example.+* Support for subtitle++### Migration++* Add a tag folder under `db`
panda.cabal view
@@ -1,5 +1,5 @@ Name: panda-Version: 0.0.0.2+Version: 0.0.0.3 Build-type: Simple Synopsis: Simple Static Blog Engine Description: Simple Static Blog Engine@@ -11,26 +11,34 @@ Cabal-version: >= 1.2 category: Web homepage: http://github.com/nfjinjing/panda/+data-files: readme.markdown, changelog.markdown library- build-depends: base, cgi, network, pandoc >= 0.46, old-locale, old-time, directory, filepath, containers >= 0.1.0.2, mps >= 0.0, parsedate >= 3000.0.0, rss >= 3000.1.0, xhtml >= 3000.2.0.0, kibro >= 0.0+ build-depends: base, cgi, network, haskell98, pandoc >= 0.46, old-locale, old-time, directory, filepath, containers >= 0.1.0.2, mps >= 0.0, parsedate >= 3000.0.0, rss >= 3000.1.0, xhtml >= 3000.2.0.0, kibro >= 0.0, HaXml >= 1.19.4 exposed-modules: Panda Panda.Controller.Application Panda.Config.Global+ Panda.Helper.Helper+ Panda.Helper.Escape Panda.Helper.Env+ Panda.Model.Post Panda.Model.Tag+ Panda.Type.Pager Panda.Type.State+ Panda.View.RSS Panda.View.Theme.BluePrint.Post Panda.View.Theme.BluePrint.Static- Panda.View.Theme.BluePrint.Template- Panda.View.Theme.BluePrint.Header- Panda.View.Theme.BluePrint.Body- Panda.View.Theme.BluePrint.Footer- Panda.View.Theme.BluePrint.Sidebar Panda.View.Theme.BluePrint.Helper Panda.View.Theme.BluePrint.Tag+ Panda.View.Theme.BluePrint.Search+ Panda.View.Theme.BluePrint.Template.Template+ Panda.View.Theme.BluePrint.Template.Header+ Panda.View.Theme.BluePrint.Template.Body+ Panda.View.Theme.BluePrint.Template.Footer+ Panda.View.Theme.BluePrint.Template.Sidebar+
+ readme.markdown view
@@ -0,0 +1,123 @@+Panda static blogging engine+==============================++Sample file db structure+-------------------------++ db+ |---- blog+ | |---- 08-09-01 first post+ | |---- 09-09-02 learn javascript+ |+ |---- tag+ |---- programming+ |---- funny++Why+----++* Blog entries are stored locally in markdown format ( can be changed easily with more hacking )+* Remote server is a mirror of local repository+* No server side API for publishing / editing, just use Darcs, Git or any SCM+* No need to find external hosting of your videos, images or sound. Put them in public folder ( still managed by SCM if you wish )+* Embed any HTML snippets+* Statically typed HTML combinator = valid html+* Based on Kibro+* Freshman level MVC design++Quick Demo+-----------++It powers [my blog](http://jinjing.blog.easymic.com) ;)++Note: the demo might run on an experimental branch, so differences from the cabal version could occur.+ +Status+-------++Panda is in its early stage. I got tired of importing / exporting blogs when switching blogging platforms, +I needed a way to not be bounded to any service API, and that's the main design goal for Panda. There's no theming yet+(except that you wish to hack the source).++The main benefit is that if I ever wanted to stop using Panda, all my blogs and resources are in manageable states.++Install+--------++### install [lighttpd](http://www.lighttpd.net/ )+++### install panda++ cabal install panda++### bootstrap++ # panda is a kibro project+ kibro new myblog+ cd myblog+ + # get a template to start+ rm -r db; rm -r public+ git clone git://github.com/nfjinjing/panda-template.git db+ sh db/scripts/bootstrap.sh+ +### run++ kibro start++It should be running on `http://127.0.0.1:3000` now.++Since Panda is based on Kibro, it helps to read how [Kibro](http://chrisdone.com/blog/2008/08/06/kibro-haskell-lighttpd-and-fastcgi/) works.++Goodies+--------++Install `Ruby` / `Rake`, then `rake -T` to see a list of helper commands.++Config+-------++Edit `db/config/site.txt`++ blog_title = My shiny blog+ host_name = yourhost.com+ author_email = your_mail@yourhost.com++Restart required. (hint: `rake r`)+++Future+------++The template db is just a starting point for you to experiment on. However, I would encourage you to keep your own db in your own SCM, that way, you will never lose your data and never have to care about application logic again.++There are many things could be done. Whatever there will be, the key idea is simplest data / folder structure, absolutely no parsing, no xml, no code is data like and stuff :)++Hacking+--------++Another way to get Panda running with source is:++ # clone the source+ git clone git://github.com/nfjinjing/panda.git myblog+ cd myblog+ + # get a template to start+ git clone git://github.com/nfjinjing/panda-template.git db+ + # manually adjust path information for lighttpd+ # edit first 6 lines of app/lighttpd/lighttpd.conf to match your path+ + # manual staging+ ln -s db/public public+ + # run+ kibro start+ +I'm heavily using the [MPS](http://github.com/nfjinjing/mps/tree/master ) package which introduces things like++ reject, join, belongs_to, match, gsub+ +and so on.+