module Rakka.Page ( PageName , Page(..) , LanguageTag , LanguageName , encodePageName , decodePageName , pageFileName' , defaultFileName , mkPageURI , mkPageFragmentURI , mkObjectURI , mkFragmentURI , mkAuxiliaryURI , mkRakkaURI ) where import Data.ByteString.Base (LazyByteString) import qualified Data.ByteString.Char8 as C8 import Data.Char import Data.Encoding import Data.Encoding.UTF8 import Data.Map (Map) import Data.Maybe import Network.HTTP.Lucu import Network.URI import Subversion.Types import System.FilePath.Posix import System.Time type PageName = String type LanguageTag = String -- See RFC 3066: http://www.ietf.org/rfc/rfc3066.txt type LanguageName = String -- i.e. "日本語" data Page = Redirection { redirName :: !PageName , redirDest :: !PageName , redirRevision :: !(Maybe RevNum) , redirLastMod :: !CalendarTime } | Entity { pageName :: !PageName , pageType :: !MIMEType , pageLanguage :: !(Maybe LanguageTag) , pageFileName :: !(Maybe String) , pageIsTheme :: !Bool -- text/css 以外では無意味 , pageIsFeed :: !Bool -- text/x-rakka 以外では無意味 , pageIsLocked :: !Bool , pageIsBoring :: !Bool , pageIsBinary :: !Bool , pageRevision :: !RevNum , pageLastMod :: !CalendarTime , pageSummary :: !(Maybe String) , pageOtherLang :: !(Map LanguageTag PageName) , pageContent :: !LazyByteString } -- UTF-8 に encode してから 0x20 - 0x7E の範圍を除いて URI escape する。 encodePageName :: PageName -> FilePath encodePageName = escapeURIString isSafeChar . C8.unpack . encode UTF8 . fixPageName where fixPageName :: PageName -> PageName fixPageName = (\ (x:xs) -> toUpper x : xs) . map (\ c -> if c == ' ' then '_' else c) isSafeChar :: Char -> Bool isSafeChar c | c == '/' = True | isReserved c = False | c > ' ' && c <= '~' = True | otherwise = False -- URI unescape して UTF-8 から decode する。 decodePageName :: FilePath -> PageName decodePageName = decode UTF8 . C8.pack . unEscapeString encodeFragment :: String -> String encodeFragment = escapeURIString isSafeChar . C8.unpack . encode UTF8 pageFileName' :: Page -> String pageFileName' page = fromMaybe (defaultFileName (pageType page) (pageName page)) (pageFileName page) defaultFileName :: MIMEType -> PageName -> String defaultFileName pType pName = let baseName = takeFileName pName in case pType of MIMEType "text" "x-rakka" _ -> baseName <.> "rakka" MIMEType "text" "css" _ -> baseName <.> "css" _ -> baseName mkPageURI :: URI -> PageName -> URI mkPageURI baseURI name = baseURI { uriPath = foldl () "/" [uriPath baseURI, encodePageName name ++ ".html"] } mkPageFragmentURI :: URI -> PageName -> String -> URI mkPageFragmentURI baseURI name fragment = baseURI { uriPath = foldl () "/" [uriPath baseURI, encodePageName name ++ ".html"] , uriFragment = ('#' : encodeFragment fragment) } mkFragmentURI :: String -> URI mkFragmentURI fragment = nullURI { uriFragment = ('#' : encodeFragment fragment) } mkObjectURI :: URI -> PageName -> URI mkObjectURI baseURI name = mkAuxiliaryURI baseURI ["object"] name mkAuxiliaryURI :: URI -> [String] -> PageName -> URI mkAuxiliaryURI baseURI basePath name = baseURI { uriPath = foldl () "/" ([uriPath baseURI] ++ basePath ++ [encodePageName name]) } mkRakkaURI :: PageName -> URI mkRakkaURI name = URI { uriScheme = "rakka:" , uriAuthority = Nothing , uriPath = encodePageName name , uriQuery = "" , uriFragment = "" }