1 module Rakka.Resource.Search
6 import qualified Codec.Binary.UTF8.String as UTF8
8 import Control.Arrow.ArrowIO
9 import Control.Arrow.ArrowIf
10 import Control.Arrow.ArrowList
11 import Control.Arrow.ArrowTree
12 import Control.Monad.Trans
14 import Network.HTTP.Lucu
15 import Network.URI hiding (query, fragment)
16 import Rakka.Environment
20 import Rakka.SystemConfig
22 import Rakka.Wiki.Engine
23 import System.FilePath
24 import Text.HyperEstraier hiding (getText)
25 import Text.XML.HXT.Arrow.Namespace
26 import Text.XML.HXT.Arrow.XmlArrow
27 import Text.XML.HXT.Arrow.XmlNodeSet
28 import Text.XML.HXT.DOM.TypeDefs
31 resSearch :: Environment -> ResourceDef
34 resUsesNativeThread = False
36 , resGet = Just $ handleSearch env
44 resultsPerSection :: Int
45 resultsPerSection = 10
48 maxSectionWindowSize :: Int
49 maxSectionWindowSize = 10
53 <searchResult query="foo bar baz"
59 aaa <hit>foo</hit> bbb
65 handleSearch :: Environment -> Resource ()
67 = do params <- getQueryForm
69 let query = UTF8.decodeString $ fromMaybe "" $ lookup "q" params
71 $ fmap read $ lookup "from" params
72 to = fromMaybe (from + resultsPerSection)
73 $ fmap read $ lookup "to" params
75 cond <- liftIO $ mkCond query from to
76 result <- searchPages (envStorage env) cond
78 let to' = min (from + length (srPages result)) to
80 BaseURI baseURI <- getSysConf (envSysConf env)
81 runIdempotentA baseURI $ proc ()
82 -> do tree <- ( eelem "/"
83 += ( eelem "searchResult"
84 += sattr "query" query
85 += sattr "from" (show from)
86 += sattr "to" (show to')
87 += sattr "total" (show $ srTotal result)
88 += ( constL (srPages result)
94 returnA -< outputXmlPage' tree (searchResultToXHTML env)
96 mkCond :: String -> Int -> Int -> IO Condition
98 = do cond <- newCondition
101 setMax cond (to - from)
104 mkPageElem :: ArrowXml a => a HitPage XmlTree
105 mkPageElem = ( eelem "page"
106 += attr "name" (arr hpPageName >>> mkText)
113 mkSnippetTree :: ArrowXml a => a SnippetFragment XmlTree
114 mkSnippetTree = proc fragment
116 Boundary -> eelem "boundary"
117 NormalText t -> txt t
118 HighlightedWord w -> eelem "hit" += txt w
122 searchResultToXHTML :: (ArrowXml a, ArrowChoice a, ArrowIO a) => Environment -> a XmlTree XmlTree
123 searchResultToXHTML env
125 -> do SiteName siteName <- getSysConfA (envSysConf env) -< ()
126 BaseURI baseURI <- getSysConfA (envSysConf env) -< ()
127 StyleSheet styleSheet <- getSysConfA (envSysConf env) -< ()
128 GlobalLock isGLocked <- getSysConfA (envSysConf env) -< ()
130 let cssHref = [uriToString id (mkObjectURI baseURI styleSheet) ""]
131 scriptSrc = [uriToString id (baseURI { uriPath = uriPath baseURI </> "js" }) ""]
133 pageTitle <- listA (readSubPage env) -< (Nothing, Nothing, "PageTitle")
134 leftSideBar <- listA (readSubPage env) -< (Nothing, Nothing, "SideBar/Left")
135 rightSideBar <- listA (readSubPage env) -< (Nothing, Nothing, "SideBar/Right")
139 += sattr "xmlns" "http://www.w3.org/1999/xhtml"
144 += getXPathTreesInDoc "/searchResult/@query/text()"
149 += sattr "rel" "stylesheet"
150 += sattr "type" "text/css"
151 += attr "href" (arr id >>> mkText)
153 += ( constL scriptSrc
156 += sattr "type" "text/javascript"
157 += attr "src" (arr id >>> mkText)
160 += sattr "type" "text/javascript"
161 += txt ("Rakka.baseURI=\"" ++ uriToString id baseURI "" ++ "\";")
162 += txt ("Rakka.isGlobalLocked=" ++ trueOrFalse isGLocked ++ ";")
163 += txt "Rakka.isSpecialPage=true;"
168 += sattr "class" "header"
171 += sattr "class" "center"
173 += sattr "class" "title"
177 += sattr "class" "body"
179 += txt "Search Result"
182 += sattr "class" "searchStat"
183 += txt "Search result for "
185 += sattr "class" "queryString"
186 += getXPathTreesInDoc "/searchResult/@query/text()"
189 += getXPathTreesInDoc "/searchResult/@total/text()"
192 += ( getXPathTreesInDoc "/searchResult/page"
196 += ( ( ( getXPathTreesInDoc "/searchResult/@query/text()"
201 ( getXPathTreesInDoc "/searchResult/@from/text()"
205 arr ((`div` resultsPerSection) . read)
208 ( getXPathTreesInDoc "/searchResult/@total/text()"
212 arr ((+ 1) . (`div` resultsPerSection) . read)
216 ( ((> 1) . snd . snd)
224 += sattr "class" "footer"
227 += sattr "class" "left sideBar"
229 += sattr "class" "content"
230 += constL leftSideBar
234 += sattr "class" "right sideBar"
236 += sattr "class" "content"
237 += constL rightSideBar
242 uniqueNamespacesFromDeclAndQNames
245 formatItem :: (ArrowXml a, ArrowChoice a) => URI -> a XmlTree XmlTree
248 += sattr "class" "searchResult"
250 += attr "href" ( getAttrValue "name"
252 arr (\ x -> uriToString id (mkPageURI baseURI x) "")
256 += (getAttrValue "name" >>> mkText)
261 choiceA [ isText :-> this
262 , hasName "boundary" :-> txt " ... "
263 , hasName "hit" :-> ( eelem "span"
264 += sattr "class" "highlighted"
272 formatPager :: (ArrowChoice a, ArrowXml a) => URI -> a (String, (Int, Int)) XmlTree
275 += sattr "class" "pager"
287 proc (query, (currentSection, section))
288 -> if currentSection == section then
292 += sattr "class" "currentSection"
293 += (arr show >>> mkText)
299 += attr "href" ( mkSectionURI baseURI
303 += (arr (show . snd) >>> mkText)
304 ) -< (query, section)
308 mkSectionWindow :: ArrowList a => a (Int, Int) Int
310 = proc (currentSection, totalSections)
311 -> let windowWidth = min maxSectionWindowSize totalSections
312 windowBegin = currentSection - (windowWidth `div` 2)
313 (begin, end) = if windowBegin < 0 then
317 if windowBegin + windowWidth >= totalSections then
319 (totalSections - windowWidth, totalSections - 1)
322 (windowBegin, windowBegin + windowWidth - 1)
324 arrL id -< [begin .. end]
327 mkSectionURI :: Arrow a => URI -> a (String, Int) URI
329 = arr $ \ (query, section)
331 uriPath = uriPath baseURI </> "search"
332 , uriQuery = '?' : mkQueryString [ ("q" , query)
333 , ("from", show $ section * resultsPerSection)
334 , ("to" , show $ (section + 1) * resultsPerSection - 1)
338 uriToText :: ArrowXml a => a URI XmlTree
339 uriToText = arr (\ uri -> uriToString id uri "") >>> mkText
341 mkQueryString :: [(String, String)] -> String
342 mkQueryString [] = ""
343 mkQueryString ((k, v) : xs) = encode k ++ "=" ++ encode v ++
347 ';' : mkQueryString(xs)
349 encode :: String -> String
350 encode = escapeURIString isSafeChar . UTF8.encodeString
353 readSubPage :: (ArrowXml a, ArrowChoice a, ArrowIO a) =>
355 -> a (Maybe PageName, Maybe XmlTree, PageName) XmlTree
357 = proc (mainPageName, mainPage, subPageName) ->
358 do subPage <- getPageA (envStorage env) >>> arr fromJust >>> xmlizePage -< (subPageName, Nothing)
359 subXHTML <- makeSubXHTML (envStorage env) (envSysConf env) (envInterpTable env)
360 -< (mainPageName, mainPage, subPage)