postLst :: Pattern -> Identifier -> Context String -> ([Item String] -> Compiler [Item String]) -> Compiler String
postLst pattern template context sortFilter = do
    posts   <- return =<< sortFilter =<< loadAll (pattern .&&. hasNoVersion)
    itemTpl <- loadBody template
    applyTemplateList itemTpl (teaserField "teaser" "teaser" `mappend` context) posts

postList :: Pattern -> ([Item String] -> Compiler [Item String]) -> Compiler String
postList searchPattern = postLst searchPattern "templates/post-item.html" postCtx

dateExtractor :: String -> Item String -> Compiler String
dateExtractor format p = do
  utcTime <- getItemUTC defaultTimeLocale $ itemIdentifier p
  return $ formatTime defaultTimeLocale format utcTime

postListByMonth :: Tags -> Pattern -> ([Item String] -> Compiler [Item String]) -> Compiler String
postListByMonth tags pattern filterFun = do
  posts <- filterFun =<< loadAll (pattern .&&. hasNoVersion)
  itemTpl <- loadBody "templates/month-item.html"
  monthTpl <- loadBody "templates/month.html"
  yearTpl <- loadBody "templates/year.html"
  bucketedTemplates posts [(yearTpl, dateExtractor "%Y", id), (monthTpl, dateExtractor "%m", convertMonth)] itemTpl (taggedPostCtx tags `mappend` dateField "day" "%d")
         
bucketedTemplates :: [Item String] -> [(Template, Item String -> Compiler String, String -> String)] -> Template -> Context String -> Compiler String
bucketedTemplates posts [] itemTemplate ctx            =  applyTemplateList itemTemplate ctx posts
bucketedTemplates posts ((template, extractor, converter):xs) itemTemplate ctx = concatMap itemBody <$>
                                                                      (mapM (\((orderProp, pst)) -> applyTemplate template ((constField "orderProp" (converter orderProp)) `mappend` (bodyField "postsByMonth")) pst) =<< 
                                                                      mapM (\((orderProp, bucket)) -> liftM (orderProp,) (makeItem =<< bucketedTemplates bucket xs itemTemplate ctx)) =<<
                                                                      bucketsM extractor posts)

convertMonth :: String -> String
convertMonth month = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"] !! ((read month) - 1)

bucketsM :: (Monad m, Ord b) => (a -> m b) -> [a] -> m [(b, [a])]
bucketsM f xs = return . reverse
                       . map (first head . unzip)
                       . groupBy ( (==) `on` fst)
                       . sortBy (comparing fst) =<< mapM (\x -> liftM (, x) (f x)) xs
                             