packages feed

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 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 ""
Panda/View/Theme/BluePrint/Template/Footer.hs view
@@ -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 --------