1 module Rakka.Wiki.Engine
10 import Control.Arrow.ArrowIO
11 import Control.Arrow.ArrowList
13 import qualified Data.Map as M
15 import Network.HTTP.Lucu
19 import Rakka.SystemConfig
22 import Rakka.Wiki.Parser
23 import Rakka.Wiki.Formatter
24 import Rakka.Wiki.Interpreter
25 import Text.HyperEstraier hiding (getText)
26 import Text.ParserCombinators.Parsec
27 import Text.XML.HXT.Arrow.XmlArrow hiding (err)
28 import Text.XML.HXT.Arrow.XmlNodeSet
29 import Text.XML.HXT.DOM.TypeDefs
32 type InterpTable = Map String Interpreter
35 wikifyPage :: (ArrowXml a, ArrowChoice a) => InterpTable -> a XmlTree WikiPage
36 wikifyPage interpTable
38 -> do pName <- getXPathTreesInDoc "/page/@name/text()" >>> getText -< tree
39 pType <- getXPathTreesInDoc "/page/@type/text()" >>> getText >>> arr read -< tree
40 pFileName <- maybeA (getXPathTreesInDoc "/page/fileName/text()" >>> getText) -< tree
41 textData <- maybeA (getXPathTreesInDoc "/page/textData/text()" >>> getText) -< tree
42 base64Data <- maybeA (getXPathTreesInDoc "/page/binaryData/text()" >>> getText) -< tree
44 let dataURI = fmap (binToURI pType) base64Data
47 MIMEType "text" "x-rakka" _
48 -> case parse (wikiPage cmdTypeOf) "" (fromJust textData) of
49 Left err -> wikifyParseError -< err
50 Right xs -> returnA -< xs
53 -- <img src="data:image/png;base64,..." />
54 -> returnA -< [ Paragraph [Image (Left $ fromJust dataURI) Nothing] ]
56 _ -> if isJust dataURI then
57 -- <a href="data:application/zip;base64,...">foo.zip</a>
58 returnA -< [ Paragraph [ Anchor
59 [("href", show dataURI)]
60 [Text (fromMaybe (defaultFileName pType pName) pFileName)]
65 returnA -< [ Preformatted [Text $ fromJust textData] ]
67 cmdTypeOf :: String -> Maybe CommandType
69 = fmap commandType (M.lookup name interpTable)
71 binToURI :: MIMEType -> String -> URI
72 binToURI pType base64Data
75 , uriPath = show pType ++ ";base64," ++ (stripWhiteSpace base64Data)
78 stripWhiteSpace :: String -> String
79 stripWhiteSpace [] = []
80 stripWhiteSpace (x:xs)
81 | x `elem` " \t\n" = stripWhiteSpace xs
82 | otherwise = x : stripWhiteSpace xs
85 makeMainXHTML :: (ArrowXml a, ArrowChoice a, ArrowIO a) =>
90 makeMainXHTML sto sysConf interpTable
92 -> do BaseURI baseURI <- getSysConfA sysConf -< ()
93 wiki <- wikifyPage interpTable -< tree
94 pName <- getXPathTreesInDoc "/page/@name/text()" >>> getText -< tree
95 interpreted <- interpretCommands sto sysConf interpTable
96 -< (pName, Just (tree, wiki), wiki)
97 formatWikiBlocks -< (baseURI, interpreted)
100 makeSubXHTML :: (ArrowXml a, ArrowChoice a, ArrowIO a) =>
104 -> a (PageName, Maybe XmlTree, XmlTree) XmlTree
105 makeSubXHTML sto sysConf interpTable
106 = proc (mainPageName, mainPage, subPage)
107 -> do BaseURI baseURI <- getSysConfA sysConf -< ()
108 mainWiki <- case mainPage of
110 -> do wiki <- wikifyPage interpTable -< page
111 returnA -< Just (page, wiki)
113 -> returnA -< Nothing
114 subWiki <- wikifyPage interpTable -< subPage
115 interpreted <- interpretCommands sto sysConf interpTable
116 -< (mainPageName, mainWiki, subWiki)
117 formatWikiBlocks -< (baseURI, interpreted)
120 interpretCommands :: (ArrowXml a, ArrowChoice a, ArrowIO a) =>
124 -> a (PageName, Maybe (XmlTree, WikiPage), WikiPage) WikiPage
125 interpretCommands sto sysConf interpTable
126 = proc (name, mainPageAndWiki, targetWiki)
127 -> let ctx = InterpreterContext {
129 , ctxMainPage = fmap fst mainPageAndWiki
130 , ctxMainWiki = fmap snd mainPageAndWiki
131 , ctxTargetWiki = targetWiki
133 , ctxSysConf = sysConf
136 arrIO2 (mapM . interpBlock) -< (ctx, targetWiki)
138 interpElem :: InterpreterContext -> Element -> IO Element
139 interpElem ctx (Block b) = interpBlock ctx b >>= return . Block
140 interpElem ctx (Inline i) = interpInline ctx i >>= return . Inline
142 interpBlock :: InterpreterContext -> BlockElement -> IO BlockElement
143 interpBlock ctx (List lType lItems) = mapM (interpListItem ctx) lItems >>= return . List lType
144 interpBlock ctx (DefinitionList defs) = mapM (interpDefinition ctx) defs >>= return . DefinitionList
145 interpBlock ctx (Preformatted inlines) = mapM (interpInline ctx) inlines >>= return . Preformatted
146 interpBlock ctx (Paragraph inlines) = mapM (interpInline ctx) inlines >>= return . Paragraph
147 interpBlock ctx (Div attrs elems) = mapM (interpElem ctx) elems >>= return . Div attrs
148 interpBlock ctx (BlockCmd bcmd) = interpBlockCommand ctx bcmd
149 interpBlock _ x = return x
151 interpInline :: InterpreterContext -> InlineElement -> IO InlineElement
152 interpInline ctx (Italic inlines) = mapM (interpInline ctx) inlines >>= return . Italic
153 interpInline ctx (Bold inlines) = mapM (interpInline ctx) inlines >>= return . Bold
154 interpInline ctx (Span attrs inlines) = mapM (interpInline ctx) inlines >>= return . Span attrs
155 interpInline ctx (Anchor attrs inlines) = mapM (interpInline ctx) inlines >>= return . Anchor attrs
156 interpInline ctx (InlineCmd icmd) = interpInlineCommand ctx icmd
157 interpInline _ x = return x
159 interpListItem :: InterpreterContext -> ListItem -> IO ListItem
160 interpListItem = mapM . interpElem
162 interpDefinition :: InterpreterContext -> Definition -> IO Definition
163 interpDefinition ctx (Definition term desc)
164 = do term' <- mapM (interpInline ctx) term
165 desc' <- mapM (interpInline ctx) desc
166 return (Definition term' desc')
168 interpBlockCommand :: InterpreterContext -> BlockCommand -> IO BlockElement
169 interpBlockCommand ctx cmd
170 = case M.lookup (bCmdName cmd) interpTable of
172 -> fail ("no such interpreter: " ++ bCmdName cmd)
175 -> bciInterpret interp ctx cmd
179 interpInlineCommand :: InterpreterContext -> InlineCommand -> IO InlineElement
180 interpInlineCommand ctx cmd
181 = case M.lookup (iCmdName cmd) interpTable of
183 -> fail ("no such interpreter: " ++ iCmdName cmd)
186 -> iciInterpret interp ctx cmd
191 makeDraft :: (ArrowXml a, ArrowChoice a, ArrowIO a) => InterpTable -> a XmlTree Document
192 makeDraft interpTable
194 do redir <- maybeA (getXPathTreesInDoc "/page/@redirect") -< tree
196 Nothing -> makeEntityDraft -< tree
197 Just _ -> makeRedirectDraft -< tree
199 makeEntityDraft :: (ArrowXml a, ArrowChoice a, ArrowIO a) => a XmlTree Document
202 do doc <- arrIO0 newDocument -< ()
204 pName <- getXPathTreesInDoc "/page/@name/text()" >>> getText -< tree
205 pType <- getXPathTreesInDoc "/page/@type/text()" >>> getText -< tree
206 pLastMod <- getXPathTreesInDoc "/page/@lastModified/text()" >>> getText -< tree
207 pIsLocked <- getXPathTreesInDoc "/page/@isLocked/text()" >>> getText -< tree
208 pIsBoring <- getXPathTreesInDoc "/page/@isBoring/text()" >>> getText -< tree
209 pIsBinary <- getXPathTreesInDoc "/page/@isBinary/text()" >>> getText -< tree
210 pRevision <- getXPathTreesInDoc "/page/@revision/text()" >>> getText -< tree
211 pLang <- maybeA (getXPathTreesInDoc "/page/@lang/text()" >>> getText) -< tree
212 pFileName <- maybeA (getXPathTreesInDoc "/page/@fileName/text()" >>> getText) -< tree
213 pIsTheme <- maybeA (getXPathTreesInDoc "/page/@isTheme/text()" >>> getText) -< tree
214 pIsFeed <- maybeA (getXPathTreesInDoc "/page/@isFeed/text()" >>> getText) -< tree
215 pSummary <- maybeA (getXPathTreesInDoc "/page/summary/text()" >>> getText) -< tree
217 arrIO2 setURI -< (doc, Just $ mkRakkaURI pName)
218 arrIO2 (flip setAttribute "@title" ) -< (doc, Just pName)
219 arrIO2 (flip setAttribute "@type" ) -< (doc, Just pType)
220 arrIO2 (flip setAttribute "@mdate" ) -< (doc, Just pLastMod)
221 arrIO2 (flip setAttribute "@lang" ) -< (doc, pLang)
222 arrIO2 (flip setAttribute "rakka:fileName") -< (doc, pFileName)
223 arrIO2 (flip setAttribute "rakka:isLocked") -< (doc, Just pIsLocked)
224 arrIO2 (flip setAttribute "rakka:isBoring") -< (doc, Just pIsBoring)
225 arrIO2 (flip setAttribute "rakka:isBinary") -< (doc, Just pIsBinary)
226 arrIO2 (flip setAttribute "rakka:revision") -< (doc, Just pRevision)
227 arrIO2 (flip setAttribute "rakka:summary" ) -< (doc, pSummary)
229 arrIO2 addHiddenText -< (doc, pName)
232 Just s -> arrIO2 addHiddenText -< (doc, s)
233 Nothing -> returnA -< ()
235 -- otherLang はリンク先ページ名を hidden text で入れる。
236 otherLangs <- listA (getXPathTreesInDoc "/page/otherLang/link/@page/text()" >>> getText) -< tree
237 listA ( (arr fst &&& arrL snd)
242 ) -< (doc, otherLangs)
245 MIMEType "text" "css" _
246 -> arrIO2 (flip setAttribute "rakka:isTheme") -< (doc, pIsTheme)
248 MIMEType "text" "x-rakka" _
249 -- wikify して興味のある部分を addText する。
250 -> do arrIO2 (flip setAttribute "rakka:isFeed") -< (doc, pIsFeed)
251 wiki <- wikifyPage interpTable -< tree
252 arrIO2 (mapM_ . addBlockText) -< (doc, wiki)
259 makeRedirectDraft :: (ArrowXml a, ArrowChoice a, ArrowIO a) => a XmlTree Document
262 do doc <- arrIO0 newDocument -< ()
264 pName <- getXPathTreesInDoc "/page/@name/text()" >>> getText -< tree
265 pRedir <- getXPathTreesInDoc "/page/@redirect/text()" >>> getText -< tree
266 pRevision <- getXPathTreesInDoc "/page/@revision/text()" >>> getText -< tree
267 pLastMod <- getXPathTreesInDoc "/page/@lastModified/text()" >>> getText -< tree
269 arrIO2 setURI -< (doc, Just $ mkRakkaURI pName)
270 arrIO2 (flip setAttribute "@title" ) -< (doc, Just pName)
271 arrIO2 (flip setAttribute "@type" ) -< (doc, Just "application/x-rakka-redirection")
272 arrIO2 (flip setAttribute "@mdate" ) -< (doc, Just pLastMod)
273 arrIO2 (flip setAttribute "rakka:revision") -< (doc, Just pRevision)
275 -- リダイレクト先ページ名はテキストとして入れる
276 arrIO2 addText -< (doc, pRedir)
280 addElemText :: Document -> Element -> IO ()
281 addElemText doc (Block b) = addBlockText doc b
282 addElemText doc (Inline i) = addInlineText doc i
284 addBlockText :: Document -> BlockElement -> IO ()
285 addBlockText doc (Heading _ text) = addText doc text
286 addBlockText _ HorizontalLine = return ()
287 addBlockText doc (List _ items) = mapM_ (addListItemText doc) items
288 addBlockText doc (DefinitionList defs) = mapM_ (addDefinitionText doc) defs
289 addBlockText doc (Preformatted inlines) = mapM_ (addInlineText doc) inlines
290 addBlockText doc (Paragraph inlines) = mapM_ (addInlineText doc) inlines
291 addBlockText doc (Div _ elems) = mapM_ (addElemText doc) elems
292 addBlockText _ EmptyBlock = return ()
293 addBlockText doc (BlockCmd bcmd) = addBlockCmdText doc bcmd
295 addInlineText :: Document -> InlineElement -> IO ()
296 addInlineText doc (Text text) = addText doc text
297 addInlineText doc (Italic inlines) = mapM_ (addInlineText doc) inlines
298 addInlineText doc (Bold inlines) = mapM_ (addInlineText doc) inlines
299 addInlineText doc (ObjectLink page Nothing) = addText doc page
300 addInlineText doc (ObjectLink page (Just text)) = addHiddenText doc page
302 addInlineText doc (PageLink page fragm Nothing) = addText doc (fromMaybe "" page ++ fromMaybe "" fragm)
303 addInlineText doc (PageLink page fragm (Just text)) = addHiddenText doc (fromMaybe "" page ++ fromMaybe "" fragm)
305 addInlineText doc (ExternalLink uri Nothing) = addText doc (uriToString id uri "")
306 addInlineText doc (ExternalLink uri (Just text)) = addHiddenText doc (uriToString id uri "")
308 addInlineText _ (LineBreak _) = return ()
309 addInlineText doc (Span _ inlines) = mapM_ (addInlineText doc) inlines
310 addInlineText doc (Image src alt) = do case src of
311 Left uri -> addHiddenText doc (uriToString id uri "")
312 Right page -> addHiddenText doc page
314 Just text -> addHiddenText doc text
316 addInlineText doc (Anchor _ inlines) = mapM_ (addInlineText doc) inlines
317 addInlineText _ (Input _) = return ()
318 addInlineText _ EmptyInline = return ()
319 addInlineText doc (InlineCmd icmd) = addInlineCmdText doc icmd
321 addListItemText :: Document -> ListItem -> IO ()
322 addListItemText = mapM_ . addElemText
324 addDefinitionText :: Document -> Definition -> IO ()
325 addDefinitionText doc (Definition term desc)
326 = do mapM_ (addInlineText doc) term
327 mapM_ (addInlineText doc) desc
329 addBlockCmdText :: Document -> BlockCommand -> IO ()
330 addBlockCmdText doc (BlockCommand _ _ blocks) = mapM_ (addBlockText doc) blocks
332 addInlineCmdText :: Document -> InlineCommand -> IO ()
333 addInlineCmdText doc (InlineCommand _ _ inlines) = mapM_ (addInlineText doc) inlines
336 wikifyParseError :: Arrow a => a ParseError WikiPage
337 wikifyParseError = proc err
338 -> returnA -< [Div [("class", "error")]
339 [ Block (Preformatted [Text (show err)]) ]]