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 qualified Data.Time.W3C as W3C
14 import Network.HTTP.Lucu
15 import Network.URI hiding (path)
16 import Rakka.Environment
20 import Rakka.SystemConfig
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 name = return Nothing
32 | isLower $ head name = return Nothing -- 先頭の文字が小文字であってはならない
34 = return $ Just $ ResourceDef {
35 resUsesNativeThread = False
37 , resGet = Just $ handleGet env name
40 , resPut = Just $ handlePut env name
41 , resDelete = Just $ handleDelete env name
45 name = (dropExtension . UTF8.decodeString . joinPath) path
48 handleGet :: Environment -> PageName -> Resource ()
50 = do BaseURI baseURI <- getSysConf (envSysConf env)
51 runIdempotentA baseURI $ proc ()
52 -> do pageM <- getPageA (envStorage env) -< (name, Nothing)
55 -> do items <- getDirContentsA (envStorage env) -< (name, Nothing)
57 [] -> handlePageNotFound env -< name
58 _ -> handleGetPageListing env -< (name, items)
60 -> if isEntity page then
61 handleGetEntity env -< page
63 handleRedirect env -< page
68 Location: http://example.org/Destination.html#Redirect:Source
70 handleRedirect :: (ArrowXml a, ArrowIO a) => Environment -> a Page (Resource ())
73 -> returnA -< do mType <- getEntityType
75 MIMEType "text" "xml" _
76 -> do setContentType mType
77 [resultStr] <- liftIO $ runX ( setErrorMsgHandler False fail
83 writeDocumentToString [ (a_indent , v_1 )
84 , (a_output_encoding, utf8)
85 , (a_no_xml_pi , v_0 ) ]
89 _ -> do BaseURI baseURI <- getSysConf (envSysConf env)
90 let uri = mkPageFragmentURI
93 ("Redirect:" ++ redirName redir)
97 handleGetEntity :: (ArrowXml a, ArrowChoice a, ArrowIO a) => Environment -> a Page (Resource ())
100 -> do tree <- xmlizePage -< page
101 returnA -< outputXmlPage tree [ (MIMEType "application" "xhtml+xml" [], entityToXHTML env)
102 , (MIMEType "application" "rss+xml" [], entityToRSS env)
106 entityToXHTML :: (ArrowXml a, ArrowChoice a, ArrowIO a) => Environment -> a XmlTree XmlTree
109 -> do SiteName siteName <- getSysConfA (envSysConf env) -< ()
110 BaseURI baseURI <- getSysConfA (envSysConf env) -< ()
111 StyleSheet styleSheet <- getSysConfA (envSysConf env) -< ()
112 GlobalLock isGLocked <- getSysConfA (envSysConf env) -< ()
114 name <- (getXPathTreesInDoc "/page/@name/text()" >>> getText) -< page
115 isLocked <- (getXPathTreesInDoc "/page/@isLocked/text()" >>> getText >>> parseYesOrNo) -< page
117 let cssHref = [uriToString id (mkObjectURI baseURI styleSheet) ""]
118 scriptSrc = [uriToString id (baseURI { uriPath = uriPath baseURI </> "js" }) ""]
120 pageTitle <- listA (readSubPage env) -< (name, Just page, "PageTitle")
121 leftSideBar <- listA (readSubPage env) -< (name, Just page, "SideBar/Left")
122 rightSideBar <- listA (readSubPage env) -< (name, Just page, "SideBar/Right")
123 pageBody <- listA (makeMainXHTML (envStorage env) (envSysConf env) (envInterpTable env)) -< page
127 += sattr "xmlns" "http://www.w3.org/1999/xhtml"
128 += ( getXPathTreesInDoc "/page/@lang"
130 qattr (mkQName "xml" "lang" "")
131 ( getXPathTreesInDoc "/page/@lang/text()" )
137 += getXPathTreesInDoc "/page/@name/text()"
142 += sattr "rel" "stylesheet"
143 += sattr "type" "text/css"
144 += attr "href" (arr id >>> mkText)
147 += ( constL scriptSrc
150 += sattr "type" "text/javascript"
151 += attr "src" (arr id >>> mkText)
154 += sattr "type" "text/javascript"
155 += txt ("Rakka.baseURI=\"" ++ uriToString id baseURI "" ++ "\";")
156 += txt ("Rakka.isLocked=" ++ trueOrFalse isLocked ++ ";")
157 += txt ("Rakka.isGlobalLocked=" ++ trueOrFalse isGLocked ++ ";")
159 += mkGlobalJSList env
163 += sattr "class" "header"
166 += sattr "class" "center"
168 += sattr "class" "title"
172 += sattr "class" "body"
177 += sattr "class" "footer"
180 += sattr "class" "left sideBar"
182 += sattr "class" "content"
183 += constL leftSideBar
187 += sattr "class" "right sideBar"
189 += sattr "class" "content"
190 += constL rightSideBar
195 uniqueNamespacesFromDeclAndQNames
199 entityToRSS :: (ArrowXml a, ArrowChoice a, ArrowIO a) => Environment -> a XmlTree XmlTree
202 -> do SiteName siteName <- getSysConfA (envSysConf env) -< ()
203 BaseURI baseURI <- getSysConfA (envSysConf env) -< ()
205 name <- (getXPathTreesInDoc "/page/@name/text()" >>> getText) -< page
206 summary <- maybeA (getXPathTreesInDoc "/page/summary/text()" >>> getText) -< page
207 pages <- makePageLinkList (envStorage env) (envSysConf env) (envInterpTable env) -< page
211 += sattr "xmlns" "http://purl.org/rss/1.0/"
212 += sattr "xmlns:rdf" "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
213 += sattr "xmlns:dc" "http://purl.org/dc/elements/1.1/"
214 += sattr "xmlns:trackback" "http://madskills.com/public/xml/rss/module/trackback/"
216 += sattr "rdf:about" (uriToString id (mkFeedURI baseURI name) "")
220 += getXPathTreesInDoc "/page/@name/text()"
223 += txt (uriToString id baseURI "")
225 += ( eelem "description"
226 += txt (case summary of
227 Nothing -> "RSS Feed for " ++ siteName
235 += attr "resource" (arr (mkPageURIStr baseURI) >>> mkText)
242 arr (\ n -> (n, Nothing))
244 getPageA (envStorage env)
249 += attr "rdf:about" (arr (mkPageURIStr baseURI . entityName) >>> mkText)
251 += (arr entityName >>> mkText)
254 += (arr (mkPageURIStr baseURI . entityName) >>> mkText)
256 += ( arrL (\ p -> case entitySummary p of
264 += ( arrIO (utcToLocalZonedTime . entityLastMod)
271 += ( eelem "trackback:ping"
272 += attr "rdf:resource" (arr (mkTrackbackURIStr baseURI . entityName) >>> mkText)
276 uniqueNamespacesFromDeclAndQNames
279 mkPageURIStr :: URI -> PageName -> String
280 mkPageURIStr baseURI name
281 = uriToString id (mkPageURI baseURI name) ""
283 mkTrackbackURIStr :: URI -> PageName -> String
284 mkTrackbackURIStr baseURI name
285 = uriToString id (mkAuxiliaryURI baseURI ["trackback"] name) ""
288 readSubPage :: (ArrowXml a, ArrowChoice a, ArrowIO a) =>
290 -> a (PageName, Maybe XmlTree, PageName) XmlTree
292 = proc (mainPageName, mainPage, subPageName) ->
293 do langM <- case mainPage of
295 -> returnA -< Nothing
297 -> maybeA (getXPathTreesInDoc "/page/@lang/text()" >>> getText) -< p
298 subPage <- getPageA (envStorage env) >>> arr fromJust -< (subPageName, Nothing)
299 localSubPage <- case langM of
301 -> returnA -< subPage
303 -> localize (envStorage env) -< (l, subPage)
304 subPageXml <- xmlizePage -< localSubPage
305 subXHTML <- makeSubXHTML (envStorage env) (envSysConf env) (envInterpTable env)
306 -< (Just mainPageName, mainPage, subPageXml)
309 localize :: (ArrowChoice a, ArrowIO a) => Storage -> a (LanguageTag, Page) Page
311 = proc (lang, origPage)
312 -> do let otherLang = entityOtherLang origPage
313 localName = M.lookup lang otherLang
316 -> returnA -< origPage
318 -> do localPage <- getPageA sto -< (ln, Nothing)
319 returnA -< case localPage of
325 <pageListing path="Foo">
326 <page name="Foo/Bar" />
327 <page name="Foo/Baz" />
330 handleGetPageListing :: (ArrowXml a, ArrowChoice a, ArrowIO a) => Environment -> a (PageName, [PageName]) (Resource ())
331 handleGetPageListing env
333 -> do tree <- ( eelem "/"
334 += ( eelem "pageListing"
335 += attr "path" (arr fst >>> mkText)
339 += attr "name" (arr id >>> mkText)
344 returnA -< outputXmlPage' tree (pageListingToXHTML env) -- FIXME: rss 對應
347 pageListingToXHTML :: (ArrowXml a, ArrowChoice a, ArrowIO a) => Environment -> a XmlTree XmlTree
348 pageListingToXHTML env
350 -> do SiteName siteName <- getSysConfA (envSysConf env) -< ()
351 BaseURI baseURI <- getSysConfA (envSysConf env) -< ()
352 StyleSheet styleSheet <- getSysConfA (envSysConf env) -< ()
353 GlobalLock isGLocked <- getSysConfA (envSysConf env) -< ()
355 name <- (getXPathTreesInDoc "/pageListing/@path/text()" >>> getText) -< pageListing
357 let cssHref = [uriToString id (mkObjectURI baseURI styleSheet) ""]
358 scriptSrc = [uriToString id (baseURI { uriPath = uriPath baseURI </> "js" }) ""]
360 pageTitle <- listA (readSubPage env) -< (name, Nothing, "PageTitle")
361 leftSideBar <- listA (readSubPage env) -< (name, Nothing, "SideBar/Left")
362 rightSideBar <- listA (readSubPage env) -< (name, Nothing, "SideBar/Right")
366 += sattr "xmlns" "http://www.w3.org/1999/xhtml"
371 += getXPathTreesInDoc "/pageListing/@path/text()"
376 += sattr "rel" "stylesheet"
377 += sattr "type" "text/css"
378 += attr "href" (arr id >>> mkText)
381 += ( constL scriptSrc
384 += sattr "type" "text/javascript"
385 += attr "src" (arr id >>> mkText)
388 += sattr "type" "text/javascript"
389 += txt ("Rakka.baseURI = \"" ++ uriToString id baseURI "" ++ "\";")
390 += txt ("Rakka.isGlobalLocked=" ++ trueOrFalse isGLocked ++ ";")
392 += mkGlobalJSList env
396 += sattr "class" "header"
399 += sattr "class" "center"
401 += sattr "class" "title"
405 += sattr "class" "body"
407 += ( getXPathTreesInDoc "/pageListing/page/@name/text()"
411 += attr "href" ( getText
413 arr (\ x -> uriToString id (mkPageURI baseURI x) "")
424 += sattr "class" "footer"
427 += sattr "class" "left sideBar"
429 += sattr "class" "content"
430 += constL leftSideBar
434 += sattr "class" "right sideBar"
436 += sattr "class" "content"
437 += constL rightSideBar
442 uniqueNamespacesFromDeclAndQNames
447 <pageNotFound name="Foo/Bar" />
449 handlePageNotFound :: (ArrowXml a, ArrowChoice a, ArrowIO a) => Environment -> a PageName (Resource ())
450 handlePageNotFound env
452 -> do tree <- ( eelem "/"
453 += ( eelem "pageNotFound"
454 += attr "name" (arr id >>> mkText)
457 returnA -< do setStatus NotFound
458 outputXmlPage' tree (notFoundToXHTML env)
461 notFoundToXHTML :: (ArrowXml a, ArrowChoice a, ArrowIO a) => Environment -> a XmlTree XmlTree
464 -> do SiteName siteName <- getSysConfA (envSysConf env) -< ()
465 BaseURI baseURI <- getSysConfA (envSysConf env) -< ()
466 StyleSheet styleSheet <- getSysConfA (envSysConf env) -< ()
467 GlobalLock isGLocked <- getSysConfA (envSysConf env) -< ()
469 name <- (getXPathTreesInDoc "/pageNotFound/@name/text()" >>> getText) -< pageNotFound
471 let cssHref = [uriToString id (mkObjectURI baseURI styleSheet) ""]
472 scriptSrc = [uriToString id (baseURI { uriPath = uriPath baseURI </> "js" }) ""]
474 pageTitle <- listA (readSubPage env) -< (name, Nothing, "PageTitle")
475 leftSideBar <- listA (readSubPage env) -< (name, Nothing, "SideBar/Left")
476 rightSideBar <- listA (readSubPage env) -< (name, Nothing, "SideBar/Right")
480 += sattr "xmlns" "http://www.w3.org/1999/xhtml"
485 += getXPathTreesInDoc "/pageNotFound/@name/text()"
490 += sattr "rel" "stylesheet"
491 += sattr "type" "text/css"
492 += attr "href" (arr id >>> mkText)
495 += ( constL scriptSrc
498 += sattr "type" "text/javascript"
499 += attr "src" (arr id >>> mkText)
502 += sattr "type" "text/javascript"
503 += txt ("Rakka.baseURI = \"" ++ uriToString id baseURI "" ++ "\";")
504 += txt ("Rakka.isGlobalLocked=" ++ trueOrFalse isGLocked ++ ";")
506 += mkGlobalJSList env
510 += sattr "class" "header"
513 += sattr "class" "center"
515 += sattr "class" "title"
519 += sattr "class" "body"
520 += txt "404 Not Found (FIXME)" -- FIXME
524 += sattr "class" "footer"
527 += sattr "class" "left sideBar"
529 += sattr "class" "content"
530 += constL leftSideBar
534 += sattr "class" "right sideBar"
536 += sattr "class" "content"
537 += constL rightSideBar
542 uniqueNamespacesFromDeclAndQNames
546 handlePut :: Environment -> PageName -> Resource ()
548 = do userID <- getUserID env
549 runXmlA env "rakka-page-1.0.rng" $ proc tree
550 -> do page <- parseXmlizedPage -< (name, tree)
551 status <- putPageA (envStorage env) -< (userID, page)
552 returnA -< setStatus status
555 handleDelete :: Environment -> PageName -> Resource ()
556 handleDelete env name
557 = do userID <- getUserID env
558 status <- deletePage (envStorage env) userID name
562 mkFeedList :: (ArrowIO a, ArrowXml a) => Environment -> a b XmlTree
564 = proc _ -> do SiteName siteName <- getSysConfA (envSysConf env) -< ()
565 BaseURI baseURI <- getSysConfA (envSysConf env) -< ()
567 feed <- unlistA <<< arrIO0 (findFeeds $ envStorage env) -< ()
570 += sattr "rel" "alternate"
571 += sattr "type" "application/rss+xml"
572 += attr "title" (txt siteName <+> txt " - " <+> mkText)
573 += attr "href" (arr (mkFeedURIStr baseURI) >>> mkText) ) -<< feed
576 findFeeds :: Storage -> IO [PageName]
578 = do cond <- newCondition
579 setPhrase cond "[UVSET]"
580 addAttrCond cond "rakka:isFeed STREQ yes"
581 setOrder cond "@uri STRA"
582 result <- searchPages sto cond
583 return (map hpPageName $ srPages result)
586 mkGlobalJSList :: (ArrowIO a, ArrowXml a, ArrowChoice a) => Environment -> a b XmlTree
588 = proc _ -> do BaseURI baseURI <- getSysConfA (envSysConf env) -< ()
590 scriptName <- unlistA <<< arrIO0 (findJavaScripts $ envStorage env) -< ()
591 pageM <- getPageA (envStorage env) -< (scriptName, Nothing)
594 Nothing -> none -< ()
597 -> ( if entityIsBinary page then
599 += sattr "type" "text/javascript"
600 += attr "src" (arr (mkObjectURIStr baseURI . pageName) >>> mkText) )
603 += sattr "type" "text/javascript"
604 += (arr (UTF8.decode . L.unpack . entityContent) >>> mkText) )
610 findJavaScripts :: Storage -> IO [PageName]
612 = do cond <- newCondition
613 setPhrase cond "[UVSET]"
614 addAttrCond cond "@title STRBW Global/"
615 addAttrCond cond "@type STRBW text/javascript"
616 setOrder cond "@uri STRA"
617 result <- searchPages sto cond
618 return (map hpPageName $ srPages result)
621 mkFeedURIStr :: URI -> PageName -> String
622 mkFeedURIStr baseURI name
623 = uriToString id (mkFeedURI baseURI name) ""
626 mkObjectURIStr :: URI -> PageName -> String
627 mkObjectURIStr baseURI name
628 = uriToString id (mkObjectURI baseURI name) ""