diff --git a/Panda/Config/Global.hs b/Panda/Config/Global.hs
--- a/Panda/Config/Global.hs
+++ b/Panda/Config/Global.hs
@@ -16,7 +16,7 @@
 
 db_id       = "db"
 flat_id     = "."
-blog_id     = "blog"
+post_id     = "blog"
 config_id   = "config"
 tag_id      = "tag"
 comment_id  = "comment"
@@ -28,7 +28,7 @@
 
 config_uri  = flat_uri / config_id / "site.txt"
 sidebar_uri = flat_uri / config_id / sidebar_id
-blog_uri    = flat_uri / blog_id
+post_uri    = flat_uri / post_id
 tag_uri     = flat_uri / tag_id
 comment_uri = flat_uri / comment_id
 theme_uri   = flat_uri / config_id / theme_id
@@ -76,3 +76,13 @@
 theme = if user_theme_uri.doesFileExist.purify
   then user_config' user_theme_uri .(("name", user_theme_name) : ) .to_theme
   else default_theme
+  
+-- custom
+as_l s                   = "[" ++ s ++ "]"
+post_date_format         = config_for' "post_date_format" "%y-%m-%d %T"
+comment_date_format      = config_for' "comment_date_format" "%y-%m-%d %T"
+url_date_format          = config_for' "url_date_format" "%y-%m-%d"
+url_date_matcher         = config_for' "url_date_matcher" "\\d{2}-\\d{2}-\\d{2}"
+url_title_subs           = config_for' "url_title_subs" "" .as_l .read :: [(String, String)]
+url_date_title_seperator = config_for' "url_date_title_seperator" " "
+
diff --git a/Panda/Controller/Application.hs b/Panda/Controller/Application.hs
--- a/Panda/Controller/Application.hs
+++ b/Panda/Controller/Application.hs
@@ -12,7 +12,7 @@
 import qualified Data.Map as Map
 
 -- env
-import Panda.Helper.Env hiding (tag)
+import Panda.Helper.Env hiding (tag, uri)
 import Panda.Helper.StateHelper
 import Prelude hiding ((.), (/), (^), id)
 import qualified Panda.Type.State as S
@@ -33,10 +33,19 @@
 import qualified Panda.View.Control.Search as SearchV
 import qualified Panda.View.Widget.RSS as RSSV
 
-per_page = G.per_page
-root     = G.root
+-- helpers
+-- liftIO: move to an (u)pper level monad
+u = liftIO
+blog_regex = G.url_date_matcher
 
-blog_regex = "\\d{2}-\\d{2}-\\d{2}"
+init_post tags x = do
+  x.Tag.fill_tag tags.Comment.fill_comment_size.u
+
+tag_list = Tag.list.u
+
+
+-- main controller
+
 pages = 
   [ ("$"                  ,index             )
   , ("(\\?.+)?$"          ,index             )
@@ -47,88 +56,80 @@
   , ("tag/."              ,tag               )
   , ("search"             ,search            )
   , ("comment/create"     ,comment_create    )
-  ] .map_fst ((root /) >>> ("^" ++))
+  ] .map_fst ((G.root /) >>> ("^" ++))
   ++
-  [ ("^" ++ root ++ "$"          ,index      )]
-
+  [ ("^" ++ G.root ++ "$"          ,index      )]
 
 
 index = do
-  blogs <- Post.list.liftIO
-  p <- paginate per_page $ blogs.length
-  tags <- Tag.list.liftIO
+  posts <- Post.list.u
+  p <- paginate posts
+  tags <- tag_list
   let nav = if p.Pager.current == 1 then home_nav else no_navigation
-  let state = S.empty { S.uid = G.blog_id, S.pager = p, S.tags = tags, S.nav_location = nav }
+  let state = S.empty { S.uid = G.post_id, S.pager = p, S.tags = tags, S.nav_location = nav }
   
-  blogs.map (Tag.fill_tag tags).mapM ( Comment.fill_comment_size >>> liftIO ) 
-    ^ PostV.list state >>= output_html
-
+  posts.mapM (init_post tags) ^ PostV.list state >>= output_html
 
 index_feed = do
-  blogs <- Post.list.liftIO
-  blogs.RSSV.rss "" "" .output
+  posts <- Post.list.u
+  posts.RSSV.rss "" "" .output
 
 blog = do
-  id <- uri ^ ( "blog" / )
-  blog <- Post.get id .liftIO
-  comments <- Comment.list_for id .liftIO
-  tags <- Tag.list.liftIO
+  id <- uri ^ Post.uri_to_id
+  blog <- Post.get id .u
+  comments <- Comment.list_for id .u
+  tags <- tag_list
   
   let state = S.empty { S.uid = id, S.tags = tags, S.resource_title = blog.resource_title }
-  blog.Tag.fill_tag tags .Comment.fill_comment_size .liftIO
-    ^ PostV.view state comments >>= output_html
+  blog.(init_post tags) ^ PostV.view state comments >>= output_html
 
 static = do
   id <- uri
-  static_page <- Static.get id .liftIO
-  tags <- Tag.list.liftIO
+  static_page <- Static.get id .u
+  tags <- tag_list
   
   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 = S.empty { S.uid = id, S.tags = tags, S.nav_location = nav, S.resource_title = static_page.resource_title }
   StaticV.view state static_page .output_html
 
-
 tag = do
   id <- uri
-  tags <- Tag.list.liftIO
+  tags <- tag_list
   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)
-      p <- paginate per_page $ blogs.length
+    Nothing -> not_found
+    Just post_set -> do
+      posts <- post_set.to_list.rsort.mapM (Post.get) .u >>= mapM (init_post tags)
+      p <- paginate posts
       let state = S.empty { S.uid = id, S.pager = p, S.tags = tags, S.resource_title = tag_name.Tag.resource_title_from_name }
-      blogs.TagV.view state.output_html
-
+      posts.TagV.view state.output_html
 
 tag_feed = do
   id <- uri ^ (split "/" >>> init >>> join "/")
-  tags <- Tag.list.liftIO
+  tags <- tag_list
   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.RSSV.rss "tag" tag_name.output
+    Nothing -> not_found
+    Just post_set -> do
+      posts <- post_set.to_list.rsort.mapM (Post.get) .u ^ map (Tag.fill_tag tags)
+      posts.RSSV.rss G.tag_id tag_name.output
 
 search = do
   s <- param_with_default "s" ""
-  blogs <- Post.search s .liftIO
+  posts <- Post.search s .u
 
-  p <- paginate per_page $ blogs.length
-  tags <- Tag.list.liftIO
+  p <- paginate posts
+  tags <- tag_list
   query <- uri
   let state = S.empty { S.uid = query, S.pager = p, S.tags = tags, S.resource_title = query }
 
-  blogs.map (Tag.fill_tag tags) .SearchV.view state s.output_html
-
-
+  posts.mapM (init_post tags) >>= \x -> x.SearchV.view state s.output_html
 
 comment_create = do
-  post_id <- input_with_default "post_id" (G.blog_id / "nothing")
-  exists <- (G.flat_uri / post_id.to_utf8) .doesFileExist .liftIO
-  let valid_path = equalFilePath G.blog_id (takeDirectory post_id)
+  post_id <- input_with_default "post_id" (G.post_id / "nothing")
+  exists <- (G.flat_uri / post_id.to_utf8) .doesFileExist .u
+  let valid_path = equalFilePath G.post_id (takeDirectory post_id)
   checked <- check_create
   
   if [checked, valid_path, exists].and
@@ -136,15 +137,15 @@
       inputs >>= (Comment.create >>> liftIO)
     else
       return ()
-  redirect $ (G.root / post_id.id_to_resource.to_utf8).urlEncode
+  redirect $ (post_id.Post.id_to_uri.to_utf8).urlEncode
 
 
 -- create helper, should be refactored to some common aspect
 check_create =
   [ validate "author"  ( length >>> (> 0))
-  , validate "url"     (const True)
+  , validate "author_link"     (const True)
   , validate "comment" ( length >>> (> 0))
-  , validate "human-hack" (is "10")
+  , validate "human_hack" (is "10")
   ] .sequence ^ and
 
 
diff --git a/Panda/Helper/Env.hs b/Panda/Helper/Env.hs
--- a/Panda/Helper/Env.hs
+++ b/Panda/Helper/Env.hs
@@ -20,7 +20,7 @@
   , module Data.Foldable
 ) where
   
-import MPS hiding (base, col, sub)
+import MPS hiding (base, col, sub, date)
 import Control.Monad hiding (join)
 import Control.Arrow ((>>>), (&&&), (***))
 import Data.List hiding (find)
diff --git a/Panda/Helper/Helper.hs b/Panda/Helper/Helper.hs
--- a/Panda/Helper/Helper.hs
+++ b/Panda/Helper/Helper.hs
@@ -13,15 +13,15 @@
 import Text.XHtml.Strict
 import Control.Monad hiding (join)
 import Data.Maybe
-
 import Panda.Type.Pager as Pager hiding (empty)
-import MPS
+import MPS hiding (date)
 import Prelude hiding ((.), (/), (^), id)
 import Char
 import Data.List
 import qualified Data.List as L 
 import Panda.Type.Reader
 import System.FilePath.Posix hiding ((<.>))
+import System.Time
 
 (/) = (</>)
 infixl 5 /
@@ -35,14 +35,14 @@
 -- controller
 raw_uri = requestURI ^ (uriPath >>> urlDecode >>> tail >>> remove_trailing_slash >>> from_utf8 )
 remove_trailing_slash s = if s.last.is '/' then s.init else s
-
+  
 params = do
   query_string <- requestURI ^ uriQuery 
   case query_string of
     '?':s -> s.formDecode .map_snd unescape_unicode_xml.return
     otherwise -> return []
 
-inputs = getInputs ^ trace' ^ map_snd (strip >>> gsub "\r\n" "\n" >>> unescape_unicode_xml) ^ trace'
+inputs = getInputs ^ map_snd (strip >>> unescape_unicode_xml)
 
 param_with_default s d = params ^ (lookup s >>> fromMaybe d )
 input_with_default s d = inputs ^ (lookup s >>> fromMaybe d )
@@ -53,7 +53,7 @@
 just_param s = get_param s ^ fromJust
 just_input s = get_input s ^ fromJust
 
-paginate length total = liftM5 ( Pager length ) current has_next has_previous next previous where
+full_paginate 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)
@@ -86,15 +86,11 @@
 
 output_html      = output ... renderHtml
 
-
-ie s = 
-  [ "<!-- [if IE]>"
-  , s.show
-  , "<![endif]-->"
-  ] .join' .primHtml
+spaced_url = gsub "/" " / "
 
 -- config
 parse_config s = s.split "," .map strip .reject null
+not_found = getVar "REQUEST_URI" >>= (fromMaybe "" >>> outputNotFound)
 
 
 -- class
@@ -110,4 +106,8 @@
 class Markable a where
     markup :: a -> Html
     
-spaced_url = gsub "/" " / "
+class Datable a where
+    date :: a -> CalendarTime
+
+class Addressable a where
+    uri :: a -> String
diff --git a/Panda/Helper/StateHelper.hs b/Panda/Helper/StateHelper.hs
--- a/Panda/Helper/StateHelper.hs
+++ b/Panda/Helper/StateHelper.hs
@@ -8,6 +8,7 @@
 import qualified Panda.Config.Global as G
 import Prelude hiding ((.), (/), (^), id)
 
+
 -- G.root = /blog
 -- raw_uri = blog/x
 -- full_uri = /blog/x
@@ -18,3 +19,19 @@
 
 uri = raw_uri ^ remove_root
 -- uri = raw_uri
+
+
+-- global
+parse_date format s = case maybe_d of
+  Nothing -> Nothing
+  Just d -> Just $ if d.ctYear < 1910 then d {ctYear = d.ctYear + 100} else d 
+  where
+    maybe_d = parseCalendarTime defaultTimeLocale format s
+  
+format_date          = formatCalendarTime defaultTimeLocale
+default_date         = parse_date "%Y-%m-%d %T" "2000-1-1 00:00:00".fromJust
+parse_post_date      = parse_date G.post_date_format
+default_parse_date s = s.parse_post_date .fromMaybe default_date
+
+-- controller
+paginate = length >>> full_paginate (G.per_page)
diff --git a/Panda/Model/Comment.hs b/Panda/Model/Comment.hs
--- a/Panda/Model/Comment.hs
+++ b/Panda/Model/Comment.hs
@@ -6,14 +6,15 @@
 import qualified Panda.Config.Global as G
 import Panda.Type.Reader
 import qualified Panda.Model.Post as Post
-import System.Time
 import System.Directory
+import Panda.Helper.StateHelper
 
 data Comment = Comment
   { uid    :: String     -- comment/08-09-04 blog title
   , author :: String
-  , url    :: String
   , body   :: String
+  , author_email :: String
+  , author_link    :: String
   }
   deriving (Show, Eq)
 
@@ -23,6 +24,10 @@
 instance Markable Comment where
   markup x  = render_to_html Markdown (x.body)
 
+instance Datable Comment where
+  date x  = x.uid.split "/".last.default_parse_date
+
+-- CRUD
 list_for post_id = do
   has_comments <- doesDirectoryExist d
   if has_comments
@@ -32,37 +37,36 @@
     d = (G.comment_uri / r)
     r = post_id.id_to_resource.to_utf8
 
-from_post_id x = Comment (post_id_to_uid x) "" "" ""
-
-post_id_to_uid x = G.flat_uri / G.comment_id / x.split "/" .last
-uid_to_post_id x = G.blog_id / x.split "/" .last
-
 get id = do
   xs <- (G.flat_uri / id.to_utf8) .readFile ^ lines
   let author = xs.first
-  let url = xs.drop 2.first
+  let author_email = ""
+  let author_link = xs.drop 2.first
   let body = xs.drop 4.unlines
-  return $ Comment id author url body
+  return $ Comment id author body author_email author_link
 
 create h = do
-  let author  = at "author"
-  let url     = at "url"
-  let body    = at "comment"
-  let post_id = at "post_id"
+  let author      = at "author"
+  let author_link = at "author_link"
+  -- let author_email = at "author_email"
+  let body        = at "comment"
+  let post_id     = at "post_id"
 
-  timestamp <- ( getClockTime >>= toCalendarTime ) ^ format_date
+  timestamp <- ( getClockTime >>= toCalendarTime ) ^ format_date G.comment_date_format
   let comment_path = post_id_to_uid post_id .to_utf8
   createDirectoryIfMissing True comment_path
   
   let uid = comment_path / timestamp
-  let content = [author, url, body].join "\n\n"
+  let content = [author, author_link, body].join "\n\n"
   writeFile uid content
   
   where at s = h.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)
+-- extra
+from_post_id x = Comment (post_id_to_uid x) "" "" "" ""
+
+post_id_to_uid x = G.flat_uri / G.comment_id / x.split "/" .last
+uid_to_post_id x = G.post_id / x.split "/" .last
 
 fill_comment_size x = do
   size <- x.Post.uid.list_for ^ length 
diff --git a/Panda/Model/Post.hs b/Panda/Model/Post.hs
--- a/Panda/Model/Post.hs
+++ b/Panda/Model/Post.hs
@@ -5,10 +5,13 @@
 module Panda.Model.Post where
 
 -- env
-import Panda.Helper.Env hiding (match, title, body)
+import Panda.Helper.Env hiding (match, title, body, date)
+import qualified MPS as MPS
 import Prelude hiding ((.), (/), (^), id, readFile)
 import qualified Panda.Config.Global as G
 import Panda.Type.Reader
+import Panda.Helper.StateHelper
+import Panda.Helper.Helper (date)
 
 data Post = Post 
   { uid :: String     -- blog/08-09-04 blog title
@@ -25,21 +28,37 @@
 
 instance Markable Post where
   markup x  = render_to_html (x.reader) (x.body)
-    
-list = ls G.blog_uri ^ map from_utf8 ^ rsort ^ map (G.blog_id /) >>= mapM get
+  
+instance Datable Post where
+  date = uid >>> get_date
 
+
+instance Addressable Post where
+  uri = uid >>> id_to_uri
+
+-- CRUD
+list = ls G.post_uri ^ map from_utf8 ^ rsort ^ map (G.post_id /) >>= mapM get
+
 get id        = liftM4 (Post id (get_title id) ) (get_body id) (return []) (return 0) (return $ get_reader id)
+get_extension = takeExtension
 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"
-date x     = x.uid.words.first.split "/".last.("20"++).parse_date.fromMaybe (parse_date "2000-1-1".fromJust)
-
+get_date id   = id.words.first.split "/".last.default_parse_date
 
 match s x = [title, body] .map (send_to x >>> lower >>> isInfixOf (s.lower)) .or
 search "" = return []
 search s  = list ^ filter (match s)
 
+-- extra
+id_to_uri id = G.root / ( pretty_date ++ G.url_date_title_seperator ++ formatted_title ++ ext ) where
+  formatted_title = G.url_title_subs.map (\(a,b) -> gsub a b).inject (id.get_title) apply
+  pretty_date     = id.get_date.format_date G.url_date_format
+  ext = id.get_extension
+
+uri_to_id s = G.post_id / (d ++ " " ++ t) where
+  (raw_d, (prefix, title_with_sep)) = s.MPS.match G.url_date_matcher.fromJust.fst
+  raw_t = title_with_sep.drop (G.url_date_title_seperator.length)
+  t = G.url_title_subs.map (\(a,b) -> gsub b a) .inject raw_t apply
+  d = raw_d.parse_date G.url_date_format .fromJust.format_date G.post_date_format
 
diff --git a/Panda/Model/Static.hs b/Panda/Model/Static.hs
--- a/Panda/Model/Static.hs
+++ b/Panda/Model/Static.hs
@@ -19,6 +19,7 @@
 instance Markable Static where
   markup x = render_to_html (x.reader) (x.body)
   
+-- CRUD
 get id        = liftM2 (Static id) (get_body id) (return $ get_reader id)
 get_title     = id_to_resource >>> dropExtension
 get_body id   = (G.flat_uri / id.to_utf8) .readFile
diff --git a/Panda/Model/Tag.hs b/Panda/Model/Tag.hs
--- a/Panda/Model/Tag.hs
+++ b/Panda/Model/Tag.hs
@@ -19,15 +19,13 @@
 instance Resource Tag where
   resource_title = uid >>> spaced_url
 
-sorted xs    = xs.sortBy(compare_by (resources >>> S.size)).reverse
-name_to_id x = G.tag_id / x
-
+-- CRUD
 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.split "/" .tail.join'
 get_resources id = (G.flat_uri / id.to_utf8) .readFile
-  ^ filter_comment ^ lines ^ map (G.blog_id / ) ^ to_set
+  ^ filter_comment ^ lines ^ map (G.post_id / ) ^ to_set
 
 resource_title_from_name = name_to_id >>> spaced_url
 tag_map' xs       = xs . map (name &&& resources) . to_h
@@ -35,3 +33,7 @@
 
 for_resource xs x = xs.select (resources >>> has x) .map name
 fill_tag xs x     = x { Post.tags = for_resource xs (x.Post.uid) }
+
+-- extra
+sorted xs    = xs.sortBy(compare_by (resources >>> S.size)).reverse
+name_to_id x = G.tag_id / x
diff --git a/Panda/View/Atom/Comment.hs b/Panda/View/Atom/Comment.hs
--- a/Panda/View/Atom/Comment.hs
+++ b/Panda/View/Atom/Comment.hs
@@ -1,8 +1,9 @@
 module Panda.View.Atom.Comment where
   
-import Panda.Helper.Env hiding (title, date)
+import Panda.Helper.Env hiding (title)
 import Prelude hiding ((.), (/), (^), id, span)
 import qualified Panda.Config.Global as G
+import Panda.Helper.StateHelper
 
 -- view
 import qualified Panda.View.Atom.Tag as Tag
@@ -21,9 +22,9 @@
   , 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
+  where a = if x.author_link.null then x.author.toHtml else toHtml $ hotlink (x.author_link) << x.author
 
-comment_date x = toHtml $ x.date.formatCalendarTime defaultTimeLocale "%b %e, %Y at %I:%M %p"
+comment_date x = toHtml $ x.date.format_date "%b %e, %Y at %I:%M %p"
 
 
 -- form
@@ -34,11 +35,12 @@
   [ h2 ! [id "respond"] << "Leave a Response"
   , gui (G.root / "comment/create") ! [id "commentform"] <<
     [ field "author" 22 1 "Name (required)"
-    , field "url" 22 2 "Website"
-    , field "human-hack" 22 3 "What is 5 + 5 ?"
+    -- , field "author_email" 22 2 "Email"
+    , field "author_link" 22 3 "Website"
+    , field "human_hack" 22 4 "What is 5 + 5 ?"
     , p << hidden "post_id" (x.uid.uid_to_post_id)
-    , p << textarea ! [name "comment", id "comment", cols "10", rows "20", strAttr "tabindex" "4"] << ""
-    , p << submit "submit" "Submit Comment" ! [strAttr "tabindex" "5"]
+    , p << textarea ! [name "comment", id "comment", cols "10", rows "20", strAttr "tabindex" "5"] << ""
+    , p << submit "submit" "Submit Comment" ! [strAttr "tabindex" "6"]
     ]
   ]
 
diff --git a/Panda/View/Atom/Post.hs b/Panda/View/Atom/Post.hs
--- a/Panda/View/Atom/Post.hs
+++ b/Panda/View/Atom/Post.hs
@@ -1,8 +1,9 @@
 module Panda.View.Atom.Post where
   
-import Panda.Helper.Env hiding (name, title, date)
+import Panda.Helper.Env hiding (name, title)
 import Prelude hiding ((.), (/), (^), id, span)
 import qualified Panda.Config.Global as G
+import Panda.Helper.StateHelper hiding (uri)
 
 -- view
 import qualified Panda.View.Atom.Tag as Tag
@@ -18,14 +19,13 @@
 entry x = div_class "post" << [entry_title, entry_mark, entry_body] where
   entry_title = x.title_link
   entry_body  = div_class "entry" << x.markup
-  entry_mark  = p ! [theclass "small"] << [ blog_date, blog_tags, blog_comments ].map (send_to x)
+  entry_mark  = p ! [theclass "small"] << [ post_date, post_tags, post_comments ].map (send_to x)
 
-title_link x              = h2 << hotlink (entry_uri x) << x.title
-entry_uri x               = G.root / ( uid x .id_to_resource )
-blog_date x               = toHtml $ x.date.formatCalendarTime defaultTimeLocale "%b %e, %Y"
+title_link x              = h2 << hotlink (x.uri) << x.title
+post_date x               = toHtml $ x.date.format_date "%b %e, %Y"
 
-blog_tags x | x.tags.null = toHtml ""
-blog_tags x = " | " +++ "Published in " +++ x.tags.map Tag.tag_link .intersperse (", ".toHtml)
+post_tags x | x.tags.null = toHtml ""
+post_tags x = " | " +++ "Published in " +++ x.tags.map Tag.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")
+post_comments x | x.comment_size.is 0 = toHtml ""
+post_comments x = " | " +++ hotlink ( x.uri / "#comments") << (x.comment_size.show ++ " Comments")
diff --git a/Panda/View/Atom/Tag.hs b/Panda/View/Atom/Tag.hs
--- a/Panda/View/Atom/Tag.hs
+++ b/Panda/View/Atom/Tag.hs
@@ -8,5 +8,5 @@
 instance DataRenderer Tag where
   render_data = name >>> tag_link
 
-tag_link s = toHtml $ hotlink (G.root / "tag" / s ) << s 
+tag_link s = toHtml $ hotlink (G.root / G.tag_id / s ) << s 
 
diff --git a/Panda/View/Widget/Helper.hs b/Panda/View/Widget/Helper.hs
--- a/Panda/View/Widget/Helper.hs
+++ b/Panda/View/Widget/Helper.hs
@@ -11,7 +11,7 @@
   if tagged 
   then
     let tag_name = Tag.get_name uid in
-      link ( "tag" / tag_name) tag_name
+      link ( G.tag_id / tag_name) tag_name
   else link "" "Home"
   where
     uid = State.uid state
diff --git a/Panda/View/Widget/RSS.hs b/Panda/View/Widget/RSS.hs
--- a/Panda/View/Widget/RSS.hs
+++ b/Panda/View/Widget/RSS.hs
@@ -4,16 +4,13 @@
 import Panda.Helper.Env -- hiding (Language)
 import Prelude hiding ((.), (/), (^), id)
 import qualified Panda.Config.Global as G
-import Panda.Type.Pager as Pager
+import qualified Panda.Type.Pager as Pager
 import Text.RSS
 import qualified Text.RSS as RSS
 
 -- model
 import Panda.Model.Post as Post
 
-
-entry_uri x = Post.uid x.split "/".tail.join "/"
-
 -- RSS
 channel_rss_template = [ RSS.Language "en-us" ]
 
@@ -22,16 +19,16 @@
   , Description $ x.markup.show
   , Author $ G.author_email
   , Link $ x.item_uri
-  , PubDate $ x.Post.date 
+  , PubDate $ x.date
   ]
 
-item_uri x = nullURI { uriScheme = "http://", uriPath = host_link (x.entry_uri) }
+item_uri x = nullURI { uriScheme = "http://", uriPath = host_link (x.uri) }
 rss_uri x  = nullURI { uriScheme = "http://", uriPath = host_link x }
 
 rss categary s xs = RSS title  (rss_uri link) title channel_rss_template (xs.take 20 .map item_rss_template)
   .rssToXML.showXML
   where
     link = categary / s
-    title = G.blog_title ++ " / " ++ s
+    title = if s.empty then G.blog_title else G.blog_title ++ " / " ++ s
 
 host_link s = G.host_name ++ (G.root / s)
diff --git a/changelog.markdown b/changelog.markdown
--- a/changelog.markdown
+++ b/changelog.markdown
@@ -1,3 +1,11 @@
+2008.10.19
+-----------
+
+### Feature
+
+* Pretty URLs
+* Configurable date format for plain text
+
 2008.10.18
 -----------
 
diff --git a/panda.cabal b/panda.cabal
--- a/panda.cabal
+++ b/panda.cabal
@@ -1,5 +1,5 @@
 Name:                 panda
-Version:              2008.10.18
+Version:              2008.10.19
 Build-type:           Simple
 Synopsis:             A simple static blog engine
 Description:          A simple static blog engine
diff --git a/readme.markdown b/readme.markdown
--- a/readme.markdown
+++ b/readme.markdown
@@ -1,9 +1,9 @@
-Panda - simple blogging engine
-==============================
+## Panda: a simple blog engine in Haskell
 
-Sample file db structure
--------------------------
+### Features
 
+#### simple file structure
+
     db
     |---- blog
     |     |---- 08-09-01 first post
@@ -15,78 +15,23 @@
     |
     |---- static
           |---- About
-  
 
-Why
-----
-
-* 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 )
-* Easy to use, just create a file and the engine takes care of the rest
-* Extensible through embedding any HTML snippets
-* Use `custom.css` for theming
-
-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.
-        
-Install
---------
-
-### install [lighttpd (version 1.4.19)](http://www.lighttpd.net/ )
-
-Note: 1.4.20 is bugged for reason still unknown yet.
-
-
-### install panda
-
-    cabal install panda
-
-### bootstrap
-
-    # panda is a kibro project
-    kibro new myblog
-    cd myblog
-    
-    # get a template to start
-    git clone git://github.com/nfjinjing/panda-template.git db
-    sh db/extra/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`
+#### simple config file
 
-    blog_title   = My shiny blog
-    host_name    = yourhost.com
-    author_email = your_mail@yourhost.com
+    blog_title    = C大调
+    blog_subtitle = 野猫不吃薯片
+    host_name     = jinjing.easymic.com
+    author_email  = nfjinjing@gmail.com
 
-Restart required. (hint: `rake r`)
+    navigation    = About
+    per_page      = 7
+    sidebar       = Blogroll.md
+    favicon       = panda_icon.png
 
 
-Theme
---------
+#### clear separation of code and data
 
-Example blueprint theme in `db/config/theme/blueprint.txt`
+theme structure through, for example, `db/config/theme/blueprint.txt`:
 
     container  = container
     header     = column span-12 first
@@ -94,44 +39,27 @@
     main       = column span-9 first
     sidebar    = column span-3 last
     footer     = footer
-
+    
     css        = screen, blueprint-wp, custom
-    js         = jquery-1.2.6.min, jquery.getUrlParam, jquery.highlight-2, custom
-
+    js         = jquery-1.2.6.min, custom
 
-These are custom class names for each element block.
+theme styling inside, for example, `db/public/theme/blueprint`, where all theme specific resources, e.g. `css/js/images`, are hosted.
 
-note: `css` and `js` are required to be placed under `db/public/theme/your_theme_name/css` and `db/public/theme/your_theme_name/js`, leave out the file extensions in `site.txt`.
+#### standard web technology
 
-You can create new themes by adding theme files in `db/config/theme`, and place
+extend/customize with pure css and javascript ( note the custom part in css and js from the last section )
 
-    theme = your_new_theme
+#### hacker friendly
 
-inside `site.txt`.
+* posts are in plain text
+* SCM for publishing / data managing ( not really a feature, but a design goal from the beginning )
+* blindly follow the KISS principle from code to UI: be a simple blog, no more no less
 
-Hacking
---------
+### Try
 
-Another way to get Panda running with source is:
+Follow the [install guide](/static/panda/install)
 
-    # 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
+### Links
 
-    reject, join, belongs_to, match, gsub
-    
-and so on.
+* [Kibro](http://chrisdone.com/tag/Kibro)
+* [Panda on Jinjing's blog](http://jinjing.blog.easymic.com/tag/Panda)
