1 module Rakka.Resource.PageEntity
6 import qualified Codec.Binary.UTF8.String as UTF8
7 import Control.Monad.Trans
8 import qualified Data.ByteString.Lazy as L hiding (ByteString)
10 import qualified Data.Map as M
13 import Network.HTTP.Lucu
14 import Network.URI hiding (path)
15 import Rakka.Environment
19 import Rakka.SystemConfig
21 import Rakka.W3CDateTime
22 import Rakka.Wiki.Engine
23 import System.FilePath.Posix
24 import Text.HyperEstraier hiding (getText)
25 import Text.XML.HXT.Arrow
26 import Text.XML.HXT.XPath
29 fallbackPageEntity :: Environment -> [String] -> IO (Maybe ResourceDef)
30 fallbackPageEntity env path
31 | null path = return Nothing
32 | null $ head path = return Nothing
33 | isLower $ head $ head path = return Nothing -- 先頭の文字が小文字であってはならない
35 = return $ Just $ ResourceDef {
36 resUsesNativeThread = False
38 , resGet = Just $ handleGet env (toPageName path)
41 , resPut = Just $ handlePut env (toPageName path)
42 , resDelete = Just $ handleDelete env (toPageName path)
45 toPageName :: [String] -> PageName
46 toPageName = decodePageName . dropExtension . joinPath
49 handleGet :: Environment -> PageName -> Resource ()
51 = do BaseURI baseURI <- getSysConf (envSysConf env)
52 runIdempotentA baseURI $ proc ()
53 -> do pageM <- getPageA (envStorage env) -< (name, Nothing)
56 -> do items <- getDirContentsA (envStorage env) -< (name, Nothing)
58 [] -> handlePageNotFound env -< name
59 _ -> handleGetPageListing env -< (name, items)
61 -> if isEntity page then
62 handleGetEntity env -< page
64 handleRedirect env -< page
69 Location: http://example.org/Destination.html#Redirect:Source
71 handleRedirect :: (ArrowXml a, ArrowIO a) => Environment -> a Page (Resource ())
74 -> returnA -< do mType <- getEntityType
76 MIMEType "text" "xml" _
77 -> do setContentType mType
78 [resultStr] <- liftIO $ runX ( setErrorMsgHandler False fail
84 writeDocumentToString [ (a_indent , v_1 )
85 , (a_output_encoding, utf8)
86 , (a_no_xml_pi , v_0 ) ]
90 _ -> do BaseURI baseURI <- getSysConf (envSysConf env)
91 let uri = mkPageFragmentURI
94 ("Redirect:" ++ redirName redir)
98 handleGetEntity :: (ArrowXml a, ArrowChoice a, ArrowIO a) => Environment -> a Page (Resource ())
101 -> do tree <- xmlizePage -< page
102 returnA -< outputXmlPage tree [ (MIMEType "application" "xhtml+xml" [], entityToXHTML env)
103 , (MIMEType "application" "rss+xml" [], entityToRSS env)
107 entityToXHTML :: (ArrowXml a, ArrowChoice a, ArrowIO a) => Environment -> a XmlTree XmlTree
110 -> do SiteName siteName <- getSysConfA (envSysConf env) -< ()
111 BaseURI baseURI <- getSysConfA (envSysConf env) -< ()
112 StyleSheet styleSheet <- getSysConfA (envSysConf env) -< ()
113 GlobalLock isGLocked <- getSysConfA (envSysConf env) -< ()
115 name <- (getXPathTreesInDoc "/page/@name/text()" >>> getText) -< page
116 isLocked <- (getXPathTreesInDoc "/page/@isLocked/text()" >>> getText >>> parseYesOrNo) -< page
118 let cssHref = [uriToString id (mkObjectURI baseURI styleSheet) ""]
119 scriptSrc = [uriToString id (baseURI { uriPath = uriPath baseURI </> "js" }) ""]
121 pageTitle <- listA (readSubPage env) -< (name, Just page, "PageTitle")
122 leftSideBar <- listA (readSubPage env) -< (name, Just page, "SideBar/Left")
123 rightSideBar <- listA (readSubPage env) -< (name, Just page, "SideBar/Right")
124 pageBody <- listA (makeMainXHTML (envStorage env) (envSysConf env) (envInterpTable env)) -< page
128 += sattr "xmlns" "http://www.w3.org/1999/xhtml"
129 += ( getXPathTreesInDoc "/page/@lang"
131 qattr (mkQName "xml" "lang" "")
132 ( getXPathTreesInDoc "/page/@lang/text()" )
138 += getXPathTreesInDoc "/page/@name/text()"
143 += sattr "rel" "stylesheet"
144 += sattr "type" "text/css"
145 += attr "href" (arr id >>> mkText)
148 += ( constL scriptSrc
151 += sattr "type" "text/javascript"
152 += attr "src" (arr id >>> mkText)
155 += sattr "type" "text/javascript"
156 += txt ("Rakka.baseURI=\"" ++ uriToString id baseURI "" ++ "\";")
157 += txt ("Rakka.isLocked=" ++ trueOrFalse isLocked ++ ";")
158 += txt ("Rakka.isGlobalLocked=" ++ trueOrFalse isGLocked ++ ";")
160 += mkGlobalJSList env
164 += sattr "class" "header"
167 += sattr "class" "center"
169 += sattr "class" "title"
173 += sattr "class" "body"
178 += sattr "class" "footer"
181 += sattr "class" "left sideBar"
183 += sattr "class" "content"
184 += constL leftSideBar
188 += sattr "class" "right sideBar"
190 += sattr "class" "content"
191 += constL rightSideBar
196 uniqueNamespacesFromDeclAndQNames
200 entityToRSS :: (ArrowXml a, ArrowChoice a, ArrowIO a) => Environment -> a XmlTree XmlTree
203 -> do SiteName siteName <- getSysConfA (envSysConf env) -< ()
204 BaseURI baseURI <- getSysConfA (envSysConf env) -< ()
206 name <- (getXPathTreesInDoc "/page/@name/text()" >>> getText) -< page
207 summary <- maybeA (getXPathTreesInDoc "/page/summary/text()" >>> getText) -< page
208 pages <- makePageLinkList (envStorage env) (envSysConf env) (envInterpTable env) -< page
212 += sattr "xmlns" "http://purl.org/rss/1.0/"
213 += sattr "xmlns:rdf" "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
214 += sattr "xmlns:dc" "http://purl.org/dc/elements/1.1/"
215 += sattr "xmlns:trackback" "http://madskills.com/public/xml/rss/module/trackback/"
217 += sattr "rdf:about" (uriToString id (mkFeedURI baseURI name) "")
221 += getXPathTreesInDoc "/page/@name/text()"
224 += txt (uriToString id baseURI "")
226 += ( eelem "description"
227 += txt (case summary of
228 Nothing -> "RSS Feed for " ++ siteName
236 += attr "resource" (arr (mkPageURIStr baseURI) >>> mkText)
243 arr (\ n -> (n, Nothing))
245 getPageA (envStorage env)
250 += attr "rdf:about" (arr (mkPageURIStr baseURI . entityName) >>> mkText)
252 += (arr entityName >>> mkText)
255 += (arr (mkPageURIStr baseURI . entityName) >>> mkText)
257 += ( arrL (\ p -> case entitySummary p of
265 += ( arrIO (utcToLocalZonedTime . entityLastMod)
267 arr formatW3CDateTime
272 += ( eelem "trackback:ping"
273 += attr "rdf:resource" (arr (mkTrackbackURIStr baseURI . entityName) >>> mkText)
277 uniqueNamespacesFromDeclAndQNames
280 mkPageURIStr :: URI -> PageName -> String
281 mkPageURIStr baseURI name
282 = uriToString id (mkPageURI baseURI name) ""
284 mkTrackbackURIStr :: URI -> PageName -> String
285 mkTrackbackURIStr baseURI name
286 = uriToString id (mkAuxiliaryURI baseURI ["trackback"] name) ""
289 readSubPage :: (ArrowXml a, ArrowChoice a, ArrowIO a) =>
291 -> a (PageName, Maybe XmlTree, PageName) XmlTree
293 = proc (mainPageName, mainPage, subPageName) ->
294 do langM <- case mainPage of
296 -> returnA -< Nothing
298 -> maybeA (getXPathTreesInDoc "/page/@lang/text()" >>> getText) -< p
299 subPage <- getPageA (envStorage env) >>> arr fromJust -< (subPageName, Nothing)
300 localSubPage <- case langM of
302 -> returnA -< subPage
304 -> localize (envStorage env) -< (l, subPage)
305 subPageXml <- xmlizePage -< localSubPage
306 subXHTML <- makeSubXHTML (envStorage env) (envSysConf env) (envInterpTable env)
307 -< (Just mainPageName, mainPage, subPageXml)
310 localize :: (ArrowChoice a, ArrowIO a) => Storage -> a (LanguageTag, Page) Page
312 = proc (lang, origPage)
313 -> do let otherLang = entityOtherLang origPage
314 localName = M.lookup lang otherLang
317 -> returnA -< origPage
319 -> do localPage <- getPageA sto -< (ln, Nothing)
320 returnA -< case localPage of
326 <pageListing path="Foo">
327 <page name="Foo/Bar" />
328 <page name="Foo/Baz" />
331 handleGetPageListing :: (ArrowXml a, ArrowChoice a, ArrowIO a) => Environment -> a (PageName, [PageName]) (Resource ())
332 handleGetPageListing env
334 -> do tree <- ( eelem "/"
335 += ( eelem "pageListing"
336 += attr "path" (arr fst >>> mkText)
340 += attr "name" (arr id >>> mkText)
345 returnA -< outputXmlPage' tree (pageListingToXHTML env) -- FIXME: rss 對應
348 pageListingToXHTML :: (ArrowXml a, ArrowChoice a, ArrowIO a) => Environment -> a XmlTree XmlTree
349 pageListingToXHTML env
351 -> do SiteName siteName <- getSysConfA (envSysConf env) -< ()
352 BaseURI baseURI <- getSysConfA (envSysConf env) -< ()
353 StyleSheet styleSheet <- getSysConfA (envSysConf env) -< ()
354 GlobalLock isGLocked <- getSysConfA (envSysConf env) -< ()
356 name <- (getXPathTreesInDoc "/pageListing/@path/text()" >>> getText) -< pageListing
358 let cssHref = [uriToString id (mkObjectURI baseURI styleSheet) ""]
359 scriptSrc = [uriToString id (baseURI { uriPath = uriPath baseURI </> "js" }) ""]
361 pageTitle <- listA (readSubPage env) -< (name, Nothing, "PageTitle")
362 leftSideBar <- listA (readSubPage env) -< (name, Nothing, "SideBar/Left")
363 rightSideBar <- listA (readSubPage env) -< (name, Nothing, "SideBar/Right")
367 += sattr "xmlns" "http://www.w3.org/1999/xhtml"
372 += getXPathTreesInDoc "/pageListing/@path/text()"
377 += sattr "rel" "stylesheet"
378 += sattr "type" "text/css"
379 += attr "href" (arr id >>> mkText)
382 += ( constL scriptSrc
385 += sattr "type" "text/javascript"
386 += attr "src" (arr id >>> mkText)
389 += sattr "type" "text/javascript"
390 += txt ("Rakka.baseURI = \"" ++ uriToString id baseURI "" ++ "\";")
391 += txt ("Rakka.isGlobalLocked=" ++ trueOrFalse isGLocked ++ ";")
393 += mkGlobalJSList env
397 += sattr "class" "header"
400 += sattr "class" "center"
402 += sattr "class" "title"
406 += sattr "class" "body"
408 += ( getXPathTreesInDoc "/pageListing/page/@name/text()"
412 += attr "href" ( getText
414 arr (\ x -> uriToString id (mkPageURI baseURI x) "")
425 += sattr "class" "footer"
428 += sattr "class" "left sideBar"
430 += sattr "class" "content"
431 += constL leftSideBar
435 += sattr "class" "right sideBar"
437 += sattr "class" "content"
438 += constL rightSideBar
443 uniqueNamespacesFromDeclAndQNames
448 <pageNotFound name="Foo/Bar" />
450 handlePageNotFound :: (ArrowXml a, ArrowChoice a, ArrowIO a) => Environment -> a PageName (Resource ())
451 handlePageNotFound env
453 -> do tree <- ( eelem "/"
454 += ( eelem "pageNotFound"
455 += attr "name" (arr id >>> mkText)
458 returnA -< do setStatus NotFound
459 outputXmlPage' tree (notFoundToXHTML env)
462 notFoundToXHTML :: (ArrowXml a, ArrowChoice a, ArrowIO a) => Environment -> a XmlTree XmlTree
465 -> do SiteName siteName <- getSysConfA (envSysConf env) -< ()
466 BaseURI baseURI <- getSysConfA (envSysConf env) -< ()
467 StyleSheet styleSheet <- getSysConfA (envSysConf env) -< ()
468 GlobalLock isGLocked <- getSysConfA (envSysConf env) -< ()
470 name <- (getXPathTreesInDoc "/pageNotFound/@name/text()" >>> getText) -< pageNotFound
472 let cssHref = [uriToString id (mkObjectURI baseURI styleSheet) ""]
473 scriptSrc = [uriToString id (baseURI { uriPath = uriPath baseURI </> "js" }) ""]
475 pageTitle <- listA (readSubPage env) -< (name, Nothing, "PageTitle")
476 leftSideBar <- listA (readSubPage env) -< (name, Nothing, "SideBar/Left")
477 rightSideBar <- listA (readSubPage env) -< (name, Nothing, "SideBar/Right")
481 += sattr "xmlns" "http://www.w3.org/1999/xhtml"
486 += getXPathTreesInDoc "/pageNotFound/@name/text()"
491 += sattr "rel" "stylesheet"
492 += sattr "type" "text/css"
493 += attr "href" (arr id >>> mkText)
496 += ( constL scriptSrc
499 += sattr "type" "text/javascript"
500 += attr "src" (arr id >>> mkText)
503 += sattr "type" "text/javascript"
504 += txt ("Rakka.baseURI = \"" ++ uriToString id baseURI "" ++ "\";")
505 += txt ("Rakka.isGlobalLocked=" ++ trueOrFalse isGLocked ++ ";")
507 += mkGlobalJSList env
511 += sattr "class" "header"
514 += sattr "class" "center"
516 += sattr "class" "title"
520 += sattr "class" "body"
521 += txt "404 Not Found (FIXME)" -- FIXME
525 += sattr "class" "footer"
528 += sattr "class" "left sideBar"
530 += sattr "class" "content"
531 += constL leftSideBar
535 += sattr "class" "right sideBar"
537 += sattr "class" "content"
538 += constL rightSideBar
543 uniqueNamespacesFromDeclAndQNames
547 handlePut :: Environment -> PageName -> Resource ()
549 = do userID <- getUserID env
550 runXmlA env "rakka-page-1.0.rng" $ proc tree
551 -> do page <- parseXmlizedPage -< (name, tree)
552 status <- putPageA (envStorage env) -< (userID, page)
553 returnA -< setStatus status
556 handleDelete :: Environment -> PageName -> Resource ()
557 handleDelete env name
558 = do userID <- getUserID env
559 status <- deletePage (envStorage env) userID name
563 mkFeedList :: (ArrowIO a, ArrowXml a) => Environment -> a b XmlTree
565 = proc _ -> do SiteName siteName <- getSysConfA (envSysConf env) -< ()
566 BaseURI baseURI <- getSysConfA (envSysConf env) -< ()
568 feed <- unlistA <<< arrIO0 (findFeeds $ envStorage env) -< ()
571 += sattr "rel" "alternate"
572 += sattr "type" "application/rss+xml"
573 += attr "title" (txt siteName <+> txt " - " <+> mkText)
574 += attr "href" (arr (mkFeedURIStr baseURI) >>> mkText) ) -<< feed
577 findFeeds :: Storage -> IO [PageName]
579 = do cond <- newCondition
580 setPhrase cond "[UVSET]"
581 addAttrCond cond "rakka:isFeed STREQ yes"
582 setOrder cond "@uri STRA"
583 result <- searchPages sto cond
584 return (map hpPageName $ srPages result)
587 mkGlobalJSList :: (ArrowIO a, ArrowXml a, ArrowChoice a) => Environment -> a b XmlTree
589 = proc _ -> do BaseURI baseURI <- getSysConfA (envSysConf env) -< ()
591 scriptName <- unlistA <<< arrIO0 (findJavaScripts $ envStorage env) -< ()
592 pageM <- getPageA (envStorage env) -< (scriptName, Nothing)
595 Nothing -> none -< ()
598 -> ( if entityIsBinary page then
600 += sattr "type" "text/javascript"
601 += attr "src" (arr (mkObjectURIStr baseURI . pageName) >>> mkText) )
604 += sattr "type" "text/javascript"
605 += (arr (UTF8.decode . L.unpack . entityContent) >>> mkText) )
611 findJavaScripts :: Storage -> IO [PageName]
613 = do cond <- newCondition
614 setPhrase cond "[UVSET]"
615 addAttrCond cond "@title STRBW Global/"
616 addAttrCond cond "@type STRBW text/javascript"
617 setOrder cond "@uri STRA"
618 result <- searchPages sto cond
619 return (map hpPageName $ srPages result)
622 mkFeedURIStr :: URI -> PageName -> String
623 mkFeedURIStr baseURI name
624 = uriToString id (mkFeedURI baseURI name) ""
627 mkObjectURIStr :: URI -> PageName -> String
628 mkObjectURIStr baseURI name
629 = uriToString id (mkObjectURI baseURI name) ""