panda 0.0.0.5.1 → 2008.9.19
raw patch · 24 files changed
+317/−166 lines, 24 filesdep ~mpsdep ~parsec
Dependency ranges changed: mps, parsec
Files
- Panda/Config/Global.hs +13/−13
- Panda/Controller/Application.hs +54/−15
- Panda/Helper/Env.hs +7/−3
- Panda/Helper/Escape.hs +0/−18
- Panda/Helper/Helper.hs +30/−25
- Panda/Helper/MPSCandidate.hs +1/−0
- Panda/Model/Comment.hs +63/−0
- Panda/Model/Post.hs +10/−9
- Panda/Model/Static.hs +6/−7
- Panda/Model/Tag.hs +10/−10
- Panda/Type/Reader.hs +1/−1
- Panda/View/RSS.hs +8/−13
- Panda/View/Theme/BluePrint/Comment.hs +49/−0
- Panda/View/Theme/BluePrint/Helper.hs +1/−1
- Panda/View/Theme/BluePrint/Post.hs +11/−9
- Panda/View/Theme/BluePrint/Search.hs +2/−2
- Panda/View/Theme/BluePrint/Tag.hs +1/−1
- Panda/View/Theme/BluePrint/Template/Body.hs +7/−11
- Panda/View/Theme/BluePrint/Template/Footer.hs +5/−5
- Panda/View/Theme/BluePrint/Template/Header.hs +16/−5
- Panda/View/Theme/BluePrint/Template/Sidebar.hs +2/−2
- changelog.markdown +12/−0
- panda.cabal +5/−3
- readme.markdown +3/−13
Panda/Config/Global.hs view
@@ -1,23 +1,23 @@ module Panda.Config.Global where import Panda.Helper.Env-import Prelude hiding ((.), (/), id, readFile)+import Prelude hiding ((.), (/), (^), id, readFile) import Panda.Type.Reader -db_id = "db"-flat_id = "."-blog_id = "blog"-config_id = "config"-tag_id = "tag"--db_uri = db_id-flat_uri = db_uri / flat_id+db_id = "db"+flat_id = "."+blog_id = "blog"+config_id = "config"+tag_id = "tag"+comment_id = "comment" -config_uri = flat_uri / config_id / "site.txt"-blog_uri = flat_uri / blog_id-tag_uri = flat_uri / tag_id+db_uri = db_id+flat_uri = db_uri / flat_id -markup = markdown+config_uri = flat_uri / config_id / "site.txt"+blog_uri = flat_uri / blog_id+tag_uri = flat_uri / tag_id+comment_uri = flat_uri / comment_id -- unsafe, must restart server after changing config file, sorry about that ...
Panda/Controller/Application.hs view
@@ -2,28 +2,27 @@ -- models are made as simple and small as possible -- views are pure functions. --- I believe this leads to more robust system, and provides much more--- flexibility in both design and implementation-+-- a note on encoding:+-- IO are in utf-8 filter, internally use unicode bytes {-# OPTIONS -fno-monomorphism-restriction #-} - module Panda.Controller.Application where import qualified Data.Map as Map -- env import Panda.Helper.Env hiding (tag)-import Prelude hiding ((.), (/), id)+import Prelude hiding ((.), (/), (^), id) import Panda.Type.State hiding (pager)-import qualified Panda.Config.Global as Config+import qualified Panda.Config.Global as G import qualified Panda.Type.Pager as Pager -- model import qualified Panda.Model.Post as Post import qualified Panda.Model.Static as Static import qualified Panda.Model.Tag as Tag+import qualified Panda.Model.Comment as Comment -- view import qualified Panda.View.Theme.BluePrint.Post as PostV@@ -44,38 +43,45 @@ , ("^/tag/.*/rss.xml$" ,tag_feed ) , ("^/tag/." ,tag ) , ("^/search" ,search )+ , ("^/comment/create" ,comment_create ) ] -per_page = Config.per_page+per_page = G.per_page index = do blogs <- Post.list.liftIO p <- pager per_page $ blogs.length tags <- Tag.list.liftIO let nav = if p.Pager.current == 1 then home_nav else no_navigation- let state = State Config.blog_id p tags nav+ let state = State G.blog_id p tags nav - blogs.map (Tag.fill_tag tags) .PostV.list state .output_html+ blogs.map (Tag.fill_tag tags).mapM ( Comment.fill_comment_size >>> liftIO ) + ^ PostV.list state >>= output_html + index_feed = do blogs <- Post.list.liftIO blogs.RSSV.rss "" "" .output blog = do- id <- uri <.> ("blog/" ++)+ id <- uri ^ ("blog/" ++) blog <- Post.get id .liftIO+ comments <- Comment.list_for id .liftIO tags <- Tag.list.liftIO let state = State id Pager.empty tags no_navigation- blog.Tag.fill_tag tags .PostV.view state .output_html+ blog.Tag.fill_tag tags .Comment.fill_comment_size .liftIO+ ^ PostV.view state comments >>= output_html + static = do id <- uri static_page <- Static.get id .liftIO tags <- Tag.list.liftIO - let nav = if title.belongs_to Config.navigation then title else no_navigation where title = static_page.Static.title+ let nav_id = static_page.Static.uid.id_to_resource+ let nav = if nav_id.belongs_to G.navigation then nav_id else no_navigation let state = State id Pager.empty tags nav StaticV.view state static_page .output_html @@ -87,21 +93,23 @@ 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 <- blog_set.to_list.rsort.mapM (Post.get) .liftIO ^ map (Tag.fill_tag tags) p <- pager per_page $ blogs.length let state = State id p tags no_navigation blogs.TagV.view state.output_html + tag_feed = do- id <- uri <.> (split "/" >>> init >>> join "/")+ id <- uri ^ (split "/" >>> init >>> join "/") tags <- Tag.list.liftIO let tag_name = Tag.get_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 <- blog_set.to_list.rsort.mapM (Post.get) .liftIO ^ map (Tag.fill_tag tags) blogs.RSSV.rss "tag" tag_name.output + search = do s <- param_with_default "s" "" blogs <- Post.search s .liftIO@@ -112,3 +120,34 @@ let state = State query p tags no_navigation blogs.map (Tag.fill_tag tags) .SearchV.view state s.output_html++++comment_create = do+ post_id <- input_with_default "post_id" "blog/nothing"+ exists <- (G.flat_uri / post_id) .doesFileExist .liftIO+ let valid_path = equalFilePath "blog" (takeDirectory post_id)+ checked <- check_create+ + if [checked, valid_path, exists].and+ then+ inputs ^ to_h >>= (Comment.create >>> liftIO)+ else+ return ()+ redirect $ ("/" ++ post_id.id_to_resource.to_utf8).urlEncode+++-- create helper, should be refactored to some common aspect+check_create =+ [ validate "author" ( length >>> (> 0))+ , validate "url" (const True)+ , validate "comment" ( length >>> (> 0))+ , validate "human-hack" (is "10")+ ] .sequence ^ and+++validate s f = do+ maybe_s <- get_input s+ case maybe_s of+ Nothing -> return False+ Just v -> return $ f v
Panda/Helper/Env.hs view
@@ -12,9 +12,11 @@ , module System.Time.Parse , module Panda.Helper.Helper , module Data.Maybe- , module System.FilePath.Posix+ , module System.FilePath , module System.IO.UTF8 , module Text.XHtml.Strict+ , module System.Time+ , module System.Directory ) where import MPS hiding (base, col, sub)@@ -27,6 +29,8 @@ import System.Time.Parse import Panda.Helper.Helper import Data.Maybe-import System.FilePath.Posix hiding ((<.>))-import System.IO.UTF8 (readFile)+import System.FilePath hiding ((<.>))+import System.IO.UTF8 (readFile, writeFile) import Text.XHtml.Strict hiding (select)+import System.Time+import System.Directory
− Panda/Helper/Escape.hs
@@ -1,18 +0,0 @@-module Panda.Helper.Escape where--import Text.ParserCombinators.Parsec-import Char-import Data.Either.Utils--unicode_char :: Parser Char-unicode_char = do- char '&'- char '#'- word <- many1 digit- char ';'- return $ chr (read word)--unescape_parser :: Parser String-unescape_parser = many (unicode_char <|> anyChar)--unescape s = forceEither $ parse unescape_parser "" s
Panda/Helper/Helper.hs view
@@ -16,47 +16,60 @@ import Panda.Type.Pager as Pager hiding (empty) import MPS-import Prelude hiding ((.), (/), id)-import Panda.Helper.Escape+import Prelude hiding ((.), (/), (^), id)+import Panda.Helper.MPSCandidate import Char import Data.List import Panda.Type.Reader import System.FilePath.Posix hiding ((<.>)) (/) = (</>)+infixl 5 / +(^) = (<.>)+infixl 9 ^+ -- model take_extension = takeExtension >>> split "\\." >>> last -- controller-uri = requestURI <.> (uriPath >>> urlDecode >>> tail)- +uri = requestURI ^ (uriPath >>> urlDecode >>> tail >>> remove_trailing_slash >>> from_utf8 )+ where remove_trailing_slash s = if s.last.is '/' then s.init else s+ params = do- query_string <- requestURI <.> uriQuery+ query_string <- requestURI <.> uriQuery case query_string of- '?':s -> s.formDecode .return+ '?':s -> s.formDecode .map_snd unescape_unicode_xml.return otherwise -> return [] -param_with_default s d = params <.> (lookup s >>> fromMaybe d >>> unescape >>> strip)+inputs = getInputs ^ map_snd (strip >>> unescape_unicode_xml) -input_with_default s d = getInput s <.> (fromMaybe d >>> unescape >>> strip)+param_with_default s d = params <.> (lookup s >>> fromMaybe d )+input_with_default s d = inputs <.> (lookup s >>> fromMaybe d ) +get_param s = params ^ lookup s+get_input s = inputs ^ lookup s++just_param s = get_param s ^ fromJust+just_input s = get_input s ^ fromJust+ pager length total = liftM5 ( Pager length ) current has_next has_previous next previous where- current = param_with_default "page" "1" <.> read- has_next = current <.> ( (* length) >>> (< total.fromIntegral) )- has_previous = current <.> (> 1)- next = current <.> (+ 1)- previous = current <.> (+ (-1) )+ current = param_with_default "page" "1" ^ read+ has_next = current ^ ( (* length) >>> (< total.fromIntegral) )+ has_previous = current ^ (> 1)+ next = current ^ (+ 1)+ previous = current ^ (+ (-1) ) for_current_page p xs = xs.drop ((p.Pager.current - 1) * p.Pager.length) .take (p.Pager.length) -- id: /type/resoruce-id_to_type id = id.from_utf8.split "/" .first-id_to_resource id = id.from_utf8.split "/" .tail.join "/"-no_navigation = ""-home_nav = "Home"+id_to_type id = id.split "/" .first+id_to_resource id = id.split "/" .tail.join "/"+no_navigation = ""+home_nav = "Home" + -- view id = identifier @@ -78,12 +91,4 @@ , s.show , "<![endif]-->" ] .join' .primHtml--rss_uri x = URI {- uriScheme = "http://",- uriAuthority = Nothing,- uriPath = x / "rss.xml",- uriQuery = "",- uriFragment = ""- }
+ Panda/Helper/MPSCandidate.hs view
@@ -0,0 +1,1 @@+module Panda.Helper.MPSCandidate where
+ Panda/Model/Comment.hs view
@@ -0,0 +1,63 @@+module Panda.Model.Comment where++-- env+import Panda.Helper.Env hiding (match, title, body)+import Prelude hiding ((.), (/), (^), id, readFile, writeFile)+import qualified Panda.Config.Global as G+import qualified Data.Map as M+import Panda.Type.Reader+import qualified Panda.Model.Post as Post+import System.Time+import System.Directory++data Comment = Comment+ { uid :: String -- blog/08-09-04 blog title+ , author :: String+ , url :: String+ , body :: String+ }+ deriving (Show, Eq)++list_for post_id = do+ has_comments <- doesDirectoryExist d+ if has_comments+ then ls d ^ map from_utf8 ^ rsort ^ map (G.comment_id / r /) >>= mapM get+ else return []+ where + d = (G.comment_uri / r)+ r = post_id.id_to_resource.to_utf8+++get id = do+ xs <- (G.flat_uri / id) .readFile ^ lines+ let author = xs.first+ let url = xs.drop 2.first+ let body = xs.drop 4.unlines+ return $ Comment id author url body+++create h = do+ let author = at "author"+ let url = at "url"+ let body = at "comment"+ let post_id = at "post_id" .to_utf8++ timestamp <- ( getClockTime >>= toCalendarTime ) ^ format_date+ let comment_path = G.flat_uri / G.comment_id / (post_id.split "/".last) + createDirectoryIfMissing True comment_path+ + let uid = comment_path / timestamp+ let content = [author, url, body].join "\n\n"+ writeFile uid content+ + where at s = h.M.lookup s .fromJust++format_date x = formatCalendarTime defaultTimeLocale "%y-%m-%d %T" x+parse_date = parseCalendarTime defaultTimeLocale "%Y-%m-%d %T"+date x = x.uid.split "/".last.("20"++).parse_date.fromMaybe (parse_date "2000-1-1 00:00:00".fromJust)++fill_comment_size x = do+ size <- x.Post.uid.list_for ^ length + return $ x { Post.comment_size = size }++markup x = render_to_html Markdown (x.body)
Panda/Model/Post.hs view
@@ -6,8 +6,8 @@ -- env import Panda.Helper.Env hiding (match, title, body)-import Prelude hiding ((.), (/), id, readFile)-import qualified Panda.Config.Global as Config+import Prelude hiding ((.), (/), (^), id, readFile)+import qualified Panda.Config.Global as G import Panda.Type.Reader data Post = Post @@ -15,16 +15,17 @@ , title :: String , body :: String , tags :: [String]+ , comment_size :: Int , reader :: Reader } deriving (Show, Eq) -list = ls Config.blog_uri <.> rsort <.> map (Config.blog_id /) >>= mapM get+list = ls G.blog_uri ^ map from_utf8 ^ rsort ^ map (G.blog_id /) >>= mapM get -get id = liftM3 (Post id (get_title id) ) (get_body id) (return []) (return $ get_reader id)-get_title id = id.from_utf8.words.tail.unwords.dropExtension-get_body id = (Config.flat_uri / id) .readFile-get_reader id = id.take_extension.guess_reader.fromMaybe Config.default_reader+get id = liftM4 (Post id (get_title id) ) (get_body id) (return []) (return 0) (return $ get_reader id)+get_title id = id.words.tail.unwords.dropExtension+get_body id = (G.flat_uri / id.to_utf8) .readFile+get_reader id = id.take_extension.guess_reader.fromMaybe G.default_reader parse_date = parseCalendarTime defaultTimeLocale "%Y-%m-%d"@@ -33,6 +34,6 @@ match s x = [title, body] .map (send_to x >>> lower >>> isInfixOf (s.lower)) .or search "" = return []-search s = list <.> filter (match s)+search s = list ^ filter (match s) -markup x = render_to_html (x.reader) (x.body)+markup x = render_to_html (x.reader) (x.body)
Panda/Model/Static.hs view
@@ -2,24 +2,23 @@ -- env import Panda.Helper.Env hiding (match, body)-import Prelude hiding ((.), (/), id, readFile)-import qualified Panda.Config.Global as Config+import Prelude hiding ((.), (/), (^), id, readFile)+import qualified Panda.Config.Global as G import Panda.Type.Reader data Static = Static { uid :: String- , title :: String , body :: String- , tags :: [String] , reader :: Reader } deriving (Show, Eq) -get id = liftM3 (Static id (get_title id) ) (get_body id) (return []) (return $ get_reader id)+get id = liftM2 (Static id) (get_body id) (return $ get_reader id) get_title = id_to_resource >>> dropExtension-get_body id = (Config.flat_uri / id) .readFile-get_reader id = id.take_extension.guess_reader.fromMaybe Config.default_reader+get_body id = (G.flat_uri / id.to_utf8) .readFile+get_reader id = id.take_extension.guess_reader.fromMaybe G.default_reader +title = uid >>> get_title markup x = render_to_html (x.reader) (x.body)
Panda/Model/Tag.hs view
@@ -3,12 +3,12 @@ import qualified Panda.Model.Post as Post -- env-import Panda.Helper.Env hiding (readFile, name)-import Prelude hiding ((.), (/), id)+import Panda.Helper.Env hiding (name)+import Prelude hiding ((.), (/), (^), id, readFile) import qualified Data.Set as Set import qualified Data.Map as Map import qualified Data.List as List-import qualified Panda.Config.Global as Config+import qualified Panda.Config.Global as G data Tag = Tag @@ -19,18 +19,18 @@ deriving (Show, Eq) sorted xs = xs.List.sortBy(compare_by (resources >>> Set.size)).reverse-name_to_id x = Config.tag_id / (x.to_utf8)+name_to_id x = G.tag_id / x -list = ls Config.tag_uri <.> map (Config.tag_id /) >>= mapM get+list = ls G.tag_uri ^ map from_utf8 ^ map (G.tag_id /) >>= mapM get -get id = get_resources id <.> Tag id (get_name id)-get_name id = id.from_utf8.split "/" .tail.join'-get_resources id = (Config.flat_uri / id) .readFile -- preserve ansi file path- <.> filter_comment <.> lines <.> map (Config.blog_id / ) <.> to_set+get id = get_resources id ^ Tag id (get_name id)+get_name id = id.split "/" .tail.join'+get_resources id = (G.flat_uri / id.to_utf8) .readFile -- preserve ansi file path+ ^ filter_comment ^ lines ^ map (G.blog_id / ) ^ to_set tag_map' xs = xs . map (name &&& resources) . to_h-tag_map = list <.> tag_map'+tag_map = list ^ tag_map' 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/Reader.hs view
@@ -1,7 +1,7 @@ module Panda.Type.Reader where import MPS -import Prelude hiding ((.), (/), id)+import Prelude hiding ((.), (/), (^), id) import qualified Data.Map as Map import Text.Pandoc import Control.Arrow ((>>>))
Panda/View/RSS.hs view
@@ -2,8 +2,8 @@ -- env import Panda.Helper.Env-import Prelude hiding ((.), (/), id)-import qualified Panda.Config.Global as Config+import Prelude hiding ((.), (/), (^), id)+import qualified Panda.Config.Global as G import Panda.Type.Pager as Pager import Text.RSS @@ -11,7 +11,7 @@ import Panda.Model.Post as Post -entry_uri x = "/" ++ Post.uid x.urlEncode.split "/".tail.join "/"+entry_uri x = "/" ++ Post.uid x.split "/".tail.join "/" -- RSS channel_rss_template = [ Language "en-us" ]@@ -19,22 +19,17 @@ item_rss_template x = [ Title $ x.Post.title , Description $ x.Post.markup.show- , Author $ Config.author_email+ , Author $ G.author_email , Link $ x.item_uri , PubDate $ x.Post.date ] -item_uri x = URI - { uriScheme = "http://"- , uriAuthority = Nothing- , uriPath = Config.host_name ++ x.entry_uri- , uriQuery = ""- , uriFragment = ""- }+item_uri x = nullURI { uriScheme = "http://", uriPath = G.host_name ++ x.entry_uri }+rss_uri x = nullURI { uriScheme = "http://", uriPath = x / "rss.xml" } rss categary s xs = RSS title (rss_uri link) title channel_rss_template (xs.take 20 .map item_rss_template) .rssToXML.showXML where - link = Config.host_name / categary / s- title = Config.blog_title ++ " / " ++ s+ link = G.host_name / categary / s+ title = G.blog_title ++ " / " ++ s
+ Panda/View/Theme/BluePrint/Comment.hs view
@@ -0,0 +1,49 @@+module Panda.View.Theme.BluePrint.Comment where+ +import Panda.Helper.Env hiding (body, date)+import Prelude hiding ((.), (/), (^), id, span)+import qualified Panda.Type.State as State++import Panda.Model.Comment+import qualified Panda.Model.Post as Post++-- view+import Panda.View.Theme.BluePrint.Helper+import Panda.View.Theme.BluePrint.Template.Template+++list [] = []+list xs = + [ h2 ! [id "comments"] << "Responses"+ , olist ! [theclass "commentlist" ] << xs.zip (cycle ["comments-alt", ""]).map (splash entry)+ ]++entry alt x = li ! [theclass alt] << + [ cite << a+ , toHtml " says:"+ , br+ , p ! [theclass "small"] << comment_date x+ , x.markup+ ]+ where a = if x.url.null then x.author.toHtml else toHtml $ hotlink (x.url) << x.author++comment_date x = toHtml $ x.date.formatCalendarTime defaultTimeLocale "%b %e, %Y at %I:%M %p"++-- create = spaceHtml+create post = + [ h2 ! [id "respond"] << "Leave a Response"+ , gui "/comment/create" ! [id "commentform"] <<+ [ field "author" 22 1 "Name (required)"+ , field "url" 22 2 "Website"+ , field "human-hack" 22 3 "What is 5 + 5 ?"+ , p << hidden "post_id" (post.Post.uid)+ , p << textarea ! [name "comment", id "comment", cols "10", rows "20", strAttr "tabindex" "4"] << ""+ , p << submit "submit" "Submit Comment" ! [strAttr "tabindex" "5"]+ ]+ ]+ +field x s t m = p <<+ [ label ! [thefor x] << small << m+ , br + , textfield x ! [size (s.show), strAttr "tabindex" (t.show)]+ ]
Panda/View/Theme/BluePrint/Helper.hs view
@@ -1,7 +1,7 @@ module Panda.View.Theme.BluePrint.Helper where import Panda.Helper.Env-import Prelude hiding ((.), (/), id, span)+import Prelude hiding ((.), (/), (^), id, span) import Panda.Type.Pager as Pager
Panda/View/Theme/BluePrint/Post.hs view
@@ -2,17 +2,17 @@ -- env import Panda.Helper.Env-import Prelude hiding ((.), (/), id, span)+import Prelude hiding ((.), (/), (^), id, span) -import System.Time-import System.Locale import qualified Panda.Type.State as State -- model import Panda.Model.Post as Post+import qualified Panda.Model.Comment as Comment -- view import Panda.View.Theme.BluePrint.Helper+import qualified Panda.View.Theme.BluePrint.Comment as CommentV import Panda.View.Theme.BluePrint.Template.Template -- HTML@@ -20,22 +20,24 @@ entry x = div_class "post" << [entry_title, entry_mark, entry_body] where entry_title = x.title_link entry_body = div_class "entry" << x.Post.markup- entry_mark = p ! [theclass "small"] << - [ x.blog_date, x.blog_tags ]+ entry_mark = p ! [theclass "small"] << [ blog_date, blog_tags, blog_comments ].map (send_to x) title_link x = h2 << hotlink (entry_uri x) << x.Post.title-entry_uri x = "/" ++ Post.uid x.urlEncode.split "/".tail.join "/"+entry_uri x = "/" ++ Post.uid x .id_to_resource blog_date x = toHtml $ x.Post.date.formatCalendarTime defaultTimeLocale "%b %e, %Y" blog_tags x | x.tags.null = toHtml ""-blog_tags x | otherwise = toHtml " | " +++ toHtml ["Published in ".toHtml, x.tags.map tag_link .intersperse (", ".toHtml) .toHtml]+blog_tags x = " | " +++ "Published in " +++ x.tags.map tag_link .intersperse (", ".toHtml) +blog_comments x | x.comment_size.is 0 = toHtml ""+blog_comments x = " | " +++ hotlink (entry_uri x / "#comments") << (x.comment_size.show ++ " Comments")+ tag_link s = toHtml $ hotlink ("/tag/" ++ s ) << s -- entry view-view state x = x.entry.page state- +view state xs x = (x.entry +++ CommentV.list xs +++ CommentV.create x).page state+ -- list view list state = for_current_page p >>> map entry >>> (+++ nav p "/") >>> page state where p = state.State.pager
Panda/View/Theme/BluePrint/Search.hs view
@@ -1,7 +1,7 @@ module Panda.View.Theme.BluePrint.Search where import Panda.Helper.Env-import Prelude hiding ((.), (/), id, span)+import Prelude hiding ((.), (/), (^), id, span) import qualified Panda.Type.State as State -- view@@ -9,6 +9,6 @@ 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+view state s = for_current_page p >>> map entry >>> (+++ nav p ("/search" ++ "?s=" ++ s ++ "&")) >>> page state where p = state.State.pager
Panda/View/Theme/BluePrint/Tag.hs view
@@ -1,7 +1,7 @@ module Panda.View.Theme.BluePrint.Tag where import Panda.Helper.Env-import Prelude hiding ((.), (/), id, span)+import Prelude hiding ((.), (/), (^), id, span) import qualified Panda.Type.State as State -- view
Panda/View/Theme/BluePrint/Template/Body.hs view
@@ -2,8 +2,8 @@ -- env import Panda.Helper.Env-import Prelude hiding ((.), (/), id, span)-import qualified Panda.Config.Global as Config+import Prelude hiding ((.), (/), (^), id, span)+import qualified Panda.Config.Global as G import qualified Panda.Type.State as State -- view@@ -11,14 +11,12 @@ import Panda.View.Theme.BluePrint.Template.Footer -html_body state x = body << container << +html_body state x = body << div_class "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@@ -28,18 +26,16 @@ ] -- 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')"]+search_form = div_id "search" << form ! [action "/search", method "get"] << thediv << textfield "s" site_name = toHtml $ [ toHtml $ hotlink "/" ! [theclass "logo"] << ""- , h1 << Config.blog_title- , div_class "description" << Config.blog_subtitle+ , h1 << G.blog_title+ , div_class "description" << G.blog_subtitle ] navigation state = div_class "content" <<- ulist << Config.navigation.map (nav_item (state.State.nav_location))+ ulist << G.navigation.map (nav_item (state.State.nav_location)) nav_item nav s = li ! [theclass (home_tag ++ "page_item" ++ current)] << link s where home_tag = if s == home_nav then "first " else ""
@@ -1,19 +1,19 @@ module Panda.View.Theme.BluePrint.Template.Footer where import Panda.Helper.Env-import Prelude hiding ((.), (/), id, span)-import qualified Panda.Config.Global as Config+import Prelude hiding ((.), (/), (^), id, span)+import qualified Panda.Config.Global as G -import qualified Panda.Config.Global as Config+import qualified Panda.Config.Global as G html_footer = div_id "footer" << [ toHtml $ copyright , toHtml $ "2008 "- , toHtml $ Config.blog_title+ , toHtml $ G.blog_title , toHtml $ br , toHtml $ "Powered by "- , toHtml $ hotlink Config.panda_url << "Panda"+ , toHtml $ hotlink G.panda_url << "Panda" , toHtml $ " using " , toHtml $ hotlink "http://www.haskell.org/" << "Haskell" ]
Panda/View/Theme/BluePrint/Template/Header.hs view
@@ -1,14 +1,25 @@ module Panda.View.Theme.BluePrint.Template.Header where import Panda.Helper.Env-import Prelude hiding ((.), (/), id, span)-import qualified Panda.Config.Global as Config+import Prelude hiding ((.), (/), (^), id, span)+import qualified Panda.Config.Global as G -html_head = header << [meta_tag, title_tag, screen_css, wp_css, custom_css, rss_tag]+html_head = header << ([meta_tag, title_tag, rss_tag] ++ css_list ++ js_list ) -title_tag = thetitle << Config.blog_title+title_tag = thetitle << G.blog_title+rss_tag = rss_link "rss.xml"++-- css+css_list = [screen_css, wp_css, custom_css] screen_css = css_link "/theme/blueprint/wp/css/screen.css" wp_css = css_link "/theme/blueprint/wp/css/blueprint-wp.css" custom_css = css_link "/theme/blueprint/wp/css/custom.css"-rss_tag = rss_link "rss.xml"+++-- js+js_list = [jquery_js, jquery_param, jquery_highlight, custom_js]+jquery_js = js_link "/js/lib/jquery/jquery-1.2.6.min.js"+jquery_param = js_link "/js/lib/jquery/jquery.getUrlParam.js"+jquery_highlight = js_link "/js/lib/jquery/jquery.highlight-2.js"+custom_js = js_link "/js/custom.js"
Panda/View/Theme/BluePrint/Template/Sidebar.hs view
@@ -1,7 +1,7 @@ module Panda.View.Theme.BluePrint.Template.Sidebar where import Panda.Helper.Env hiding (rss_link)-import Prelude hiding ((.), (/), id, span)+import Prelude hiding ((.), (/), (^), id, span) import qualified Panda.Type.State as State import qualified Data.Set as Set@@ -36,4 +36,4 @@ [ h2 << "Tags" , unordList $ tags.Tag.sorted.map tag_link] -tag_link x = hotlink ("/" ++ x.uid.urlEncode) << x.Tag.name +++ (" (" ++ x.Tag.resources.Set.size.show ++ ")")+tag_link x = ( hotlink ("/" ++ x.uid) << x.Tag.name ) +++ ( " (" ++ x.Tag.resources.Set.size.show ++ ")" )
changelog.markdown view
@@ -1,3 +1,15 @@+2008.9.19+----------++### Features++* Comment system+* Change version schema++### Improvements++* uniform unicode handling ( byte8 internal, utf-8 IO )+ 0.0.0.5.1 ----------
panda.cabal view
@@ -1,5 +1,5 @@ Name: panda-Version: 0.0.0.5.1+Version: 2008.9.19 Build-type: Simple Synopsis: Simple Static Blog Engine Description: Simple Static Blog Engine@@ -14,16 +14,17 @@ data-files: readme.markdown, changelog.markdown library- build-depends: base, cgi, network, haskell98, old-locale, old-time, directory, filepath, containers >= 0.1.0.0, mps >= 0.0.0.1, parsedate >= 3000.0.0, rss >= 3000.0.1, xhtml >= 3000.2.0.0, kibro >= 0.0, utf8-string >= 0.3.1, pandoc >= 0.46, MissingH, parsec+ build-depends: base, cgi, network, haskell98, old-locale, old-time, directory, filepath, containers >= 0.1.0.0, mps >= 2008.9.19, parsedate >= 3000.0.0, rss >= 3000.0.1, xhtml >= 3000.2.0.0, kibro >= 0.0, utf8-string >= 0.3.1, pandoc >= 0.46, MissingH, parsec >= 2.1.0 exposed-modules: Panda Panda.Config.Global Panda.Controller.Application Panda.Helper.Env- Panda.Helper.Escape+ Panda.Helper.MPSCandidate Panda.Helper.Helper Panda.Model.Post Panda.Model.Static Panda.Model.Tag+ Panda.Model.Comment Panda.Type.Pager Panda.Type.Reader Panda.Type.State@@ -33,6 +34,7 @@ Panda.View.Theme.BluePrint.Search Panda.View.Theme.BluePrint.Static Panda.View.Theme.BluePrint.Tag+ Panda.View.Theme.BluePrint.Comment Panda.View.Theme.BluePrint.Template.Body Panda.View.Theme.BluePrint.Template.Footer Panda.View.Theme.BluePrint.Template.Header
readme.markdown view
@@ -20,8 +20,8 @@ Why ---- -* Blog entries are stored locally-* Write posts in Markdown, HTML, reStructuredText or even LaTeX format ( no Math equations yet )+* Blogs are stored locally+* Write in Markdown, HTML, reStructuredText or even LaTeX format ( Math equations won't render yet ) * 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 )@@ -36,14 +36,6 @@ 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.--The main benefit is that if I ever wanted to stop using Panda, all my blogs and resources are in manageable states.- Install -------- @@ -93,9 +85,7 @@ 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 :)+There are many things could be done. Whatever there will be, the key idea is simplest data / folder structure. Hacking --------