module Rakka.Wiki.Engine
( InterpTable
- , xmlizePage
, makeMainXHTML
, makeSubXHTML
, makeDraft
+ , makePreviewXHTML
)
where
import qualified Codec.Binary.Base64 as B64
-import Codec.Binary.UTF8.String
+import qualified Codec.Binary.UTF8.String as UTF8
import Control.Arrow
import Control.Arrow.ArrowIO
import Control.Arrow.ArrowList
-import qualified Data.ByteString.Lazy as L
+import qualified Data.ByteString.Lazy as Lazy
import Data.Map (Map)
import qualified Data.Map as M
import Data.Maybe
-import Data.Time
import Network.HTTP.Lucu
import Network.URI
import Rakka.Page
type InterpTable = Map String Interpreter
-{-
- <page name="Foo/Bar"
- type="text/x-rakka"
- lang="ja" -- 存在しない場合もある
- fileName="bar.rakka" -- 存在しない場合もある
- isTheme="no" -- text/css の場合のみ存在
- isFeed="no" -- text/x-rakka の場合のみ存在
- isLocked="no"
- isBinary="no"
- revision="112"> -- デフォルトでない場合のみ存在
- lastModified="2000-01-01T00:00:00">
-
- <summary>
- blah blah...
- </summary> -- 存在しない場合もある
-
- <otherLang> -- 存在しない場合もある
- <link lang="ja" page="Bar/Baz" />
- </otherLang>
-
- <!-- 何れか一方のみ -->
- <textData>
- blah blah...
- </textData>
- <binaryData>
- SKJaHKS8JK/DH8KS43JDK2aKKaSFLLS...
- </binaryData>
- </page>
--}
-xmlizePage :: (ArrowXml a, ArrowChoice a, ArrowIO a) => a Page XmlTree
-xmlizePage
- = proc page
- -> do lastMod <- arrIO (utcToLocalZonedTime . pageLastMod) -< page
- ( eelem "/"
- += ( eelem "page"
- += sattr "name" (pageName page)
- += sattr "type" (show $ pageType page)
- += ( case pageLanguage page of
- Just x -> sattr "lang" x
- Nothing -> none
- )
- += ( case pageFileName page of
- Just x -> sattr "fileName" x
- Nothing -> none
- )
- += ( case pageType page of
- MIMEType "text" "css" _
- -> sattr "isTheme" (yesOrNo $ pageIsTheme page)
- MIMEType "text" "x-rakka" _
- -> sattr "isFeed" (yesOrNo $ pageIsFeed page)
- _
- -> none
- )
- += sattr "isLocked" (yesOrNo $ pageIsLocked page)
- += sattr "isBoring" (yesOrNo $ pageIsBoring page)
- += sattr "isBinary" (yesOrNo $ pageIsBinary page)
- += sattr "revision" (show $ pageRevision page)
- += sattr "lastModified" (formatW3CDateTime lastMod)
- += ( case pageSummary page of
- Just s -> eelem "summary" += txt s
- Nothing -> none
- )
- += ( if M.null (pageOtherLang page) then
- none
- else
- selem "otherLang"
- [ eelem "link"
- += sattr "lang" lang
- += sattr "page" name
- | (lang, name) <- M.toList (pageOtherLang page) ]
- )
- += ( if pageIsBinary page then
- ( eelem "binaryData"
- += txt (B64.encode $ L.unpack $ pageContent page)
- )
- else
- ( eelem "textData"
- += txt (decode $ L.unpack $ pageContent page)
- )
- )
- )) -<< ()
-
-
wikifyPage :: (ArrowXml a, ArrowChoice a) => InterpTable -> a XmlTree WikiPage
wikifyPage interpTable
= proc tree
- -> do pName <- getXPathTreesInDoc "/page/@name/text()" >>> getText -< tree
- pType <- getXPathTreesInDoc "/page/@type/text()" >>> getText >>> arr read -< tree
- pFileName <- maybeA (getXPathTreesInDoc "/page/fileName/text()" >>> getText) -< tree
+ -> do pType <- getXPathTreesInDoc "/page/@type/text()" >>> getText >>> arr read -< tree
textData <- maybeA (getXPathTreesInDoc "/page/textData/text()" >>> getText) -< tree
base64Data <- maybeA (getXPathTreesInDoc "/page/binaryData/text()" >>> getText) -< tree
case pType of
MIMEType "text" "x-rakka" _
- -> case parse (wikiPage cmdTypeOf) "" (fromJust textData) of
+ -> case parse (wikiPage $ cmdTypeOf interpTable) "" (fromJust textData) of
Left err -> wikifyParseError -< err
Right xs -> returnA -< xs
-> returnA -< [ Paragraph [Image (Left $ fromJust dataURI) Nothing] ]
_ -> if isJust dataURI then
- -- <a href="data:application/zip;base64,...">foo.zip</a>
+ -- <a href="data:application/zip;base64,...">
+ -- application/zip
+ -- </a>
returnA -< [ Paragraph [ Anchor
[("href", show dataURI)]
- [Text (fromMaybe (defaultFileName pType pName) pFileName)]
+ [Text (show pType)]
]
]
else
-- pre
returnA -< [ Preformatted [Text $ fromJust textData] ]
where
- cmdTypeOf :: String -> Maybe CommandType
- cmdTypeOf name
- = fmap commandType (M.lookup name interpTable)
-
binToURI :: MIMEType -> String -> URI
binToURI pType base64Data
= nullURI {
| otherwise = x : stripWhiteSpace xs
+wikifyBin :: (ArrowXml a, ArrowChoice a) => InterpTable -> a (MIMEType, Lazy.ByteString) WikiPage
+wikifyBin interpTable
+ = proc (pType, pBin)
+ -> do let text = UTF8.decode $ Lazy.unpack pBin
+ dataURI = binToURI pType pBin
+
+ case pType of
+ MIMEType "text" "x-rakka" _
+ -> case parse (wikiPage $ cmdTypeOf interpTable) "" text of
+ Left err -> wikifyParseError -< err
+ Right xs -> returnA -< xs
+
+ MIMEType "image" _ _
+ -- <img src="data:image/png;base64,..." />
+ -> returnA -< [ Paragraph [Image (Left dataURI) Nothing] ]
+
+
+ _
+ -- <a href="data:application/zip;base64,...">
+ -- application/zip (19372 bytes)
+ -- </a>
+ -> returnA -< [ Paragraph [ Anchor
+ [("href", show dataURI)]
+ [Text (show pType ++
+ " (" ++
+ show (Lazy.length pBin) ++
+ " bytes)")]
+ ]
+ ]
+ where
+ binToURI :: MIMEType -> Lazy.ByteString -> URI
+ binToURI m b
+ = nullURI {
+ uriScheme = "data:"
+ , uriPath = show m ++ ";base64," ++ B64.encode (Lazy.unpack b)
+ }
+
+
+cmdTypeOf :: InterpTable -> String -> Maybe CommandType
+cmdTypeOf interpTable name
+ = fmap commandType (M.lookup name interpTable)
+
+
makeMainXHTML :: (ArrowXml a, ArrowChoice a, ArrowIO a) =>
Storage
-> SystemConfig
wiki <- wikifyPage interpTable -< tree
pName <- getXPathTreesInDoc "/page/@name/text()" >>> getText -< tree
interpreted <- interpretCommands sto sysConf interpTable
- -< (pName, Just (tree, wiki), wiki)
+ -< (pName, Just tree, Just wiki, wiki)
formatWikiBlocks -< (baseURI, interpreted)
-> returnA -< Nothing
subWiki <- wikifyPage interpTable -< subPage
interpreted <- interpretCommands sto sysConf interpTable
- -< (mainPageName, mainWiki, subWiki)
+ -< (mainPageName, fmap fst mainWiki, fmap snd mainWiki, subWiki)
+ formatWikiBlocks -< (baseURI, interpreted)
+
+
+makePreviewXHTML :: (ArrowXml a, ArrowChoice a, ArrowIO a) =>
+ Storage
+ -> SystemConfig
+ -> InterpTable
+ -> a (PageName, MIMEType, Lazy.ByteString) XmlTree
+makePreviewXHTML sto sysConf interpTable
+ = proc (name, pageType, pageBin)
+ -> do BaseURI baseURI <- getSysConfA sysConf -< ()
+ wiki <- wikifyBin interpTable -< (pageType, pageBin)
+ interpreted <- interpretCommands sto sysConf interpTable
+ -< (name, Nothing, Just wiki, wiki)
formatWikiBlocks -< (baseURI, interpreted)
Storage
-> SystemConfig
-> InterpTable
- -> a (PageName, Maybe (XmlTree, WikiPage), WikiPage) WikiPage
+ -> a (PageName, Maybe XmlTree, Maybe WikiPage, WikiPage) WikiPage
interpretCommands sto sysConf interpTable
- = proc (name, mainPageAndWiki, targetWiki)
+ = proc (name, mainPage, mainWiki, targetWiki)
-> let ctx = InterpreterContext {
ctxPageName = name
- , ctxMainPage = fmap fst mainPageAndWiki
- , ctxMainWiki = fmap snd mainPageAndWiki
+ , ctxMainPage = mainPage
+ , ctxMainWiki = mainWiki
, ctxTargetWiki = targetWiki
, ctxStorage = sto
, ctxSysConf = sysConf
makeDraft :: (ArrowXml a, ArrowChoice a, ArrowIO a) => InterpTable -> a XmlTree Document
makeDraft interpTable
= proc tree ->
- do doc <- arrIO0 newDocument -< ()
+ do redir <- maybeA (getXPathTreesInDoc "/page/@redirect") -< tree
+ case redir of
+ Nothing -> makeEntityDraft -< tree
+ Just _ -> makeRedirectDraft -< tree
+ where
+ makeEntityDraft :: (ArrowXml a, ArrowChoice a, ArrowIO a) => a XmlTree Document
+ makeEntityDraft
+ = proc tree ->
+ do doc <- arrIO0 newDocument -< ()
- pName <- getXPathTreesInDoc "/page/@name/text()" >>> getText -< tree
- pType <- getXPathTreesInDoc "/page/@type/text()" >>> getText -< tree
- pLastMod <- getXPathTreesInDoc "/page/@lastModified/text()" >>> getText -< tree
- pIsLocked <- getXPathTreesInDoc "/page/@isLocked/text()" >>> getText -< tree
- pIsBoring <- getXPathTreesInDoc "/page/@isBoring/text()" >>> getText -< tree
- pIsBinary <- getXPathTreesInDoc "/page/@isBinary/text()" >>> getText -< tree
- pRevision <- getXPathTreesInDoc "/page/@revision/text()" >>> getText -< tree
- pLang <- maybeA (getXPathTreesInDoc "/page/@lang/text()" >>> getText) -< tree
- pFileName <- maybeA (getXPathTreesInDoc "/page/@fileName/text()" >>> getText) -< tree
- pIsTheme <- maybeA (getXPathTreesInDoc "/page/@isTheme/text()" >>> getText) -< tree
- pIsFeed <- maybeA (getXPathTreesInDoc "/page/@isFeed/text()" >>> getText) -< tree
- pSummary <- maybeA (getXPathTreesInDoc "/page/summary/text()" >>> getText) -< tree
-
- arrIO2 setURI -< (doc, Just $ mkRakkaURI pName)
- arrIO2 (flip setAttribute "@title" ) -< (doc, Just pName)
- arrIO2 (flip setAttribute "@type" ) -< (doc, Just pType)
- arrIO2 (flip setAttribute "@mdate" ) -< (doc, Just pLastMod)
- arrIO2 (flip setAttribute "@lang" ) -< (doc, pLang)
- arrIO2 (flip setAttribute "rakka:fileName") -< (doc, pFileName)
- arrIO2 (flip setAttribute "rakka:isLocked") -< (doc, Just pIsLocked)
- arrIO2 (flip setAttribute "rakka:isBoring") -< (doc, Just pIsBoring)
- arrIO2 (flip setAttribute "rakka:isBinary") -< (doc, Just pIsBinary)
- arrIO2 (flip setAttribute "rakka:revision") -< (doc, Just pRevision)
- arrIO2 (flip setAttribute "rakka:summary" ) -< (doc, pSummary)
-
- arrIO2 addHiddenText -< (doc, pName)
-
- case pSummary of
- Just s -> arrIO2 addHiddenText -< (doc, s)
- Nothing -> returnA -< ()
-
- -- otherLang はリンク先ページ名を hidden text で入れる。
- otherLangs <- listA (getXPathTreesInDoc "/page/otherLang/link/@page/text()" >>> getText) -< tree
- listA ( (arr fst &&& arrL snd)
- >>>
- arrIO2 addHiddenText
- >>>
- none
- ) -< (doc, otherLangs)
-
- case read pType of
- MIMEType "text" "css" _
- -> arrIO2 (flip setAttribute "rakka:isTheme") -< (doc, pIsTheme)
+ pName <- getXPathTreesInDoc "/page/@name/text()" >>> getText -< tree
+ pType <- getXPathTreesInDoc "/page/@type/text()" >>> getText -< tree
+ pLastMod <- getXPathTreesInDoc "/page/@lastModified/text()" >>> getText -< tree
+ pIsLocked <- getXPathTreesInDoc "/page/@isLocked/text()" >>> getText -< tree
+ pIsBinary <- getXPathTreesInDoc "/page/@isBinary/text()" >>> getText -< tree
+ pRevision <- getXPathTreesInDoc "/page/@revision/text()" >>> getText -< tree
+ pLang <- maybeA (getXPathTreesInDoc "/page/@lang/text()" >>> getText) -< tree
+ pFileName <- maybeA (getXPathTreesInDoc "/page/@fileName/text()" >>> getText) -< tree
+ pIsTheme <- maybeA (getXPathTreesInDoc "/page/@isTheme/text()" >>> getText) -< tree
+ pIsFeed <- maybeA (getXPathTreesInDoc "/page/@isFeed/text()" >>> getText) -< tree
+ pSummary <- maybeA (getXPathTreesInDoc "/page/summary/text()" >>> getText) -< tree
+
+ arrIO2 setURI -< (doc, Just $ mkRakkaURI pName)
+ arrIO2 (flip setAttribute "@title" ) -< (doc, Just pName)
+ arrIO2 (flip setAttribute "@type" ) -< (doc, Just pType)
+ arrIO2 (flip setAttribute "@mdate" ) -< (doc, Just pLastMod)
+ arrIO2 (flip setAttribute "@lang" ) -< (doc, pLang)
+ arrIO2 (flip setAttribute "rakka:fileName") -< (doc, pFileName)
+ arrIO2 (flip setAttribute "rakka:isLocked") -< (doc, Just pIsLocked)
+ arrIO2 (flip setAttribute "rakka:isBinary") -< (doc, Just pIsBinary)
+ arrIO2 (flip setAttribute "rakka:revision") -< (doc, Just pRevision)
+ arrIO2 (flip setAttribute "rakka:summary" ) -< (doc, pSummary)
+
+ arrIO2 addHiddenText -< (doc, pName)
+
+ case pSummary of
+ Just s -> arrIO2 addHiddenText -< (doc, s)
+ Nothing -> returnA -< ()
+
+ -- otherLang はリンク先ページ名を hidden text で入れる。
+ otherLangs <- listA (getXPathTreesInDoc "/page/otherLang/link/@page/text()" >>> getText) -< tree
+ listA ( (arr fst &&& arrL snd)
+ >>>
+ arrIO2 addHiddenText
+ >>>
+ none
+ ) -< (doc, otherLangs)
+
+ case read pType of
+ MIMEType "text" "css" _
+ -> arrIO2 (flip setAttribute "rakka:isTheme") -< (doc, pIsTheme)
- MIMEType "text" "x-rakka" _
- -- wikify して興味のある部分を addText する。
- -> do arrIO2 (flip setAttribute "rakka:isFeed") -< (doc, pIsFeed)
- wiki <- wikifyPage interpTable -< tree
- arrIO2 (mapM_ . addBlockText) -< (doc, wiki)
+ MIMEType "text" "x-rakka" _
+ -- wikify して興味のある部分を addText する。
+ -> do arrIO2 (flip setAttribute "rakka:isFeed") -< (doc, pIsFeed)
+ wiki <- wikifyPage interpTable -< tree
+ arrIO2 (mapM_ . addBlockText) -< (doc, wiki)
- MIMEType _ _ _
- -> returnA -< ()
+ MIMEType _ _ _
+ -> returnA -< ()
+
+ returnA -< doc
+
+ makeRedirectDraft :: (ArrowXml a, ArrowChoice a, ArrowIO a) => a XmlTree Document
+ makeRedirectDraft
+ = proc tree ->
+ do doc <- arrIO0 newDocument -< ()
+
+ pName <- getXPathTreesInDoc "/page/@name/text()" >>> getText -< tree
+ pRedir <- getXPathTreesInDoc "/page/@redirect/text()" >>> getText -< tree
+ pIsLocked <- getXPathTreesInDoc "/page/@isLocked/text()" >>> getText -< tree
+ pRevision <- getXPathTreesInDoc "/page/@revision/text()" >>> getText -< tree
+ pLastMod <- getXPathTreesInDoc "/page/@lastModified/text()" >>> getText -< tree
+
+ arrIO2 setURI -< (doc, Just $ mkRakkaURI pName)
+ arrIO2 (flip setAttribute "@title" ) -< (doc, Just pName)
+ arrIO2 (flip setAttribute "@type" ) -< (doc, Just "application/x-rakka-redirection")
+ arrIO2 (flip setAttribute "@mdate" ) -< (doc, Just pLastMod)
+ arrIO2 (flip setAttribute "rakka:isLocked") -< (doc, Just pIsLocked)
+ arrIO2 (flip setAttribute "rakka:revision") -< (doc, Just pRevision)
+
+ -- リダイレクト先ページ名はテキストとして入れる
+ arrIO2 addText -< (doc, pRedir)
+
+ returnA -< doc
- returnA -< doc
- where
addElemText :: Document -> Element -> IO ()
addElemText doc (Block b) = addBlockText doc b
addElemText doc (Inline i) = addInlineText doc i