]> gitweb @ CieloNegro.org - Rakka.git/commitdiff
wrote Loader.DefaultPage but not tested yet
authorpho <pho@cielonegro.org>
Mon, 8 Oct 2007 05:43:02 +0000 (14:43 +0900)
committerpho <pho@cielonegro.org>
Mon, 8 Oct 2007 05:43:02 +0000 (14:43 +0900)
darcs-hash:20071008054302-62b54-0b71726a7063b7026f71a2af8958250ebd1193b7.gz

.boring
Rakka.cabal
Rakka/Page.hs [new file with mode: 0644]
Rakka/Page/Loader/DefaultPage.hs [new file with mode: 0644]
Rakka/Utils.hs [new file with mode: 0644]
defaultPages/Main_Page
schemas/rakka-page-1.0.rng

diff --git a/.boring b/.boring
index eac526253983c0fa29571e94f17384fb25cd72fe..d545fc24259110adea012442c37bbeb9cf801cca 100644 (file)
--- a/.boring
+++ b/.boring
@@ -48,7 +48,8 @@
 \.(obj|a|exe|so|lo|la)$
 ^\.darcs-temp-mail$
 
-.setup-config$
+^\.installed-pkg-config$
+^\.setup-config$
 ^Rakka.buildinfo$
 ^Setup$
 ^configure$
index a1c8a7181a8c3d9b082385058a08d14ea2f8a272..56bb66983ed56241c84e2971fd1b569ad9588ab3 100644 (file)
@@ -20,8 +20,16 @@ Category:
     Web
 Tested-With:
     GHC == 6.6.1
+Extensions:
+    Arrows
+GHC-Options:
+    -fwarn-unused-imports
 Build-Depends:
-    base, network, unix, Lucu
+    base, network, unix, encoding, base64-string, hxt, Lucu
+Exposed-Modules:
+    Rakka.Page
+Other-Modules:
+    Rakka.Page.Loader.DefaultPage
 Data-Files:
     defaultPages/Main_Page
     schemas/rakka-page-1.0.rng
@@ -30,3 +38,5 @@ Executable:
     rakka
 Main-Is:
     Main.hs
+GHC-Options:
+    -fwarn-unused-imports
\ No newline at end of file
diff --git a/Rakka/Page.hs b/Rakka/Page.hs
new file mode 100644 (file)
index 0000000..5647c8e
--- /dev/null
@@ -0,0 +1,40 @@
+module Rakka.Page
+    ( PageName
+    , Page(..)
+    , encodePageName
+    )
+    where
+
+import           Data.ByteString.Base (LazyByteString)
+import qualified Data.ByteString.Char8 as C8
+import           Data.Encoding
+import           Data.Encoding.UTF8
+import           Network.HTTP.Lucu
+import           Network.URI
+
+
+type PageName = String
+
+
+data Page
+    = Redirect PageName
+    | Page {
+        pageType      :: MIMEType
+      , pageIsTheme   :: Bool     -- text/css 以外では無意味
+      , pageIsFeed    :: Bool     -- text/x-rakka 以外では無意味
+      , pageIsLocked  :: Bool
+      , pageIsBoring  :: Bool
+      , pageSummary   :: Maybe String
+      , pageOtherLang :: [(String, PageName)]
+      , pageContent   :: LazyByteString
+      }
+
+
+-- UTF-8 に encode してから 0x20 - 0x7E の範圍を除いて URI escape する。
+encodePageName :: PageName -> FilePath
+encodePageName = escapeURIString shouldEscape . C8.unpack . encode UTF8 
+    where
+      shouldEscape :: Char -> Bool
+      shouldEscape c
+          | c >= ' ' && c <= '~' = False
+          | otherwise            = True
diff --git a/Rakka/Page/Loader/DefaultPage.hs b/Rakka/Page/Loader/DefaultPage.hs
new file mode 100644 (file)
index 0000000..2133217
--- /dev/null
@@ -0,0 +1,101 @@
+module Rakka.Page.Loader.DefaultPage
+    ( loadDefaultPage
+    )
+    where
+
+import qualified Codec.Binary.Base64.String as B64
+import           Control.Arrow
+import           Control.Arrow.ArrowList
+import qualified Data.ByteString.Lazy.Char8 as L8
+import           Paths_Rakka -- Cabal が用意する。
+import           Rakka.Page
+import           Rakka.Utils
+import           System.Directory
+import           Text.XML.HXT.Arrow.ReadDocument
+import           Text.XML.HXT.Arrow.XmlArrow
+import           Text.XML.HXT.Arrow.XmlIOStateArrow
+import           Text.XML.HXT.Arrow.XmlNodeSet
+import           Text.XML.HXT.DOM.TypeDefs
+import           Text.XML.HXT.DOM.XmlKeywords
+
+
+loadDefaultPage :: PageName -> IO (Maybe Page)
+loadDefaultPage pageName
+    -- ./data/Foo を探した後、Cabal で defaultPages/Foo を探す。
+    = do let pagePath = encodePageName pageName
+         isInDataDir <- doesFileExist ("./data/" ++ pagePath)
+         if isInDataDir then
+             return . Just =<< loadPageFile ("./data/" ++ pagePath)
+           else
+             do fpath       <- getDataFileName ("defaultPages/" ++ pagePath)
+                isInstalled <- doesFileExist fpath
+                if isInstalled then
+                    return . Just =<< loadPageFile fpath
+                  else
+                    return Nothing
+
+
+loadPageFile :: FilePath -> IO Page
+loadPageFile path
+    = do [page] <- runX ( constA path
+                          >>>
+                          loadPageFileA
+                        )
+         return page
+
+
+loadPageFileA :: IOStateArrow s FilePath Page
+loadPageFileA = ( readFromDocument [ (a_validate         , v_0)
+                                   , (a_check_namespaces , v_1)
+                                   , (a_remove_whitespace, v_1)
+                                   ]
+                  >>>
+                  parsePage
+                )
+
+
+parsePage :: (ArrowXml a, ArrowChoice a) => a XmlTree Page
+parsePage
+    = proc tree -> do mimeType <- (getXPathTreesInDoc "/page/@type/text()" >>> getText
+                                   >>> arr read) -< tree
+
+                      isTheme  <- (maybeA (getXPathTreesInDoc "/page/@type/text()" >>> getText)
+                                   >>> defaultTo "no"
+                                   >>> parseYesOrNo) -< tree
+                      isFeed   <- (maybeA (getXPathTreesInDoc "/page/@isFeed/text()" >>> getText)
+                                   >>> defaultTo "no"
+                                   >>> parseYesOrNo) -< tree
+                      isLocked <- (maybeA (getXPathTreesInDoc "/page/@isLocked/text()" >>> getText)
+                                   >>> defaultTo "no"
+                                   >>> parseYesOrNo) -< tree
+                      isBoring <- (maybeA (getXPathTreesInDoc "/page/@isBoring/text()" >>> getText)
+                                   >>> defaultTo "no"
+                                   >>> parseYesOrNo) -< tree
+
+                      summary <- (maybeA (getXPathTreesInDoc "/page/summary/text()"
+                                          >>> getText
+                                          >>> deleteIfEmpty)) -< tree
+                      
+                      otherLang <- listA (getXPathTreesInDoc "/page/otherLang/link"
+                                          >>>
+                                          (getAttrValue0 "lang"
+                                           &&&
+                                           getAttrValue0 "page")) -< tree
+
+                      textData   <- maybeA (getXPathTreesInDoc "/page/textData"   >>> getText) -< tree
+                      binaryData <- maybeA (getXPathTreesInDoc "/page/binaryData" >>> getText) -< tree
+
+                      let content = case (textData, binaryData) of
+                                      (Just text, _          ) -> L8.pack text
+                                      (_        , Just binary) -> L8.pack $ B64.decode binary
+
+                      returnA -< Page {
+                                    pageType      = mimeType
+                                  , pageIsTheme   = isTheme
+                                  , pageIsFeed    = isFeed
+                                  , pageIsLocked  = isLocked
+                                  , pageIsBoring  = isBoring
+                                  , pageSummary   = summary
+                                  , pageOtherLang = otherLang
+                                  , pageContent   = content
+                                  }
\ No newline at end of file
diff --git a/Rakka/Utils.hs b/Rakka/Utils.hs
new file mode 100644 (file)
index 0000000..cb77474
--- /dev/null
@@ -0,0 +1,40 @@
+module Rakka.Utils
+    ( parseYesOrNo
+    , maybeA
+    , defaultTo
+    , deleteIfEmpty
+    )
+    where
+
+import           Control.Arrow
+import           Control.Arrow.ArrowList
+
+
+parseYesOrNo :: ArrowChoice a => a String Bool
+parseYesOrNo 
+    = proc str -> do case str of
+                       "yes" -> returnA -< True
+                       "no"  -> returnA -< False
+                       _     -> returnA -< error ("Expected yes or no: " ++ str)
+
+
+maybeA :: (ArrowList a, ArrowChoice a) => a b c -> a b (Maybe c)
+maybeA a = listA a
+           >>>
+           proc xs -> case xs of
+                        []    -> returnA -< Nothing
+                        (x:_) -> returnA -< Just x
+
+
+defaultTo :: ArrowChoice a => b -> a (Maybe b) b
+defaultTo def
+    = proc m -> case m of
+                  Nothing -> returnA -< def
+                  Just x  -> returnA -< x
+
+
+deleteIfEmpty :: (ArrowList a, ArrowChoice a) => a String String
+deleteIfEmpty
+    = proc str -> do case str of
+                       "" -> none    -< ()
+                       _  -> returnA -< str
\ No newline at end of file
index 1c0a4213a8956e6154ae06cd5ba3cbd0082ff06b..1acd8ae5f8351b13fc99ba64db8a49ea1bed0771 100644 (file)
@@ -1,6 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <page xmlns="http://cielonegro.org/schema/Rakka/Page/1.0"
-      type="text/x-rakka">
+      type="text/x-rakka"
+      isBoring="yes">
+
   <textData>
     This is the main page. Hello, world!
   </textData>
index b85385b3fe7dcd6df3a8150310f599bdd303f04c..42b17336914528c3e5464a0a992fdbdce1261b18 100644 (file)
@@ -4,17 +4,93 @@
          datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"
          xmlns="http://relaxng.org/ns/structure/1.0">
 
-  <attribute name="type">
-    <text />
-  </attribute>
-
   <choice>
-    <element name="textData">
-      <text />
-    </element>
+    <!-- リダイレクトの場合 -->
+    <attribute name="redirect">
+      <!-- ページ名 -->
+      <data type="anyURI" />
+    </attribute>
+    
+    <!-- リダイレクトでない場合 -->
+    <group>
+      <attribute name="type">
+        <!-- MIME Type -->
+        <text />
+      </attribute>
+
+      <optional>
+        <!-- text/css でなければ無視される -->
+        <attribute name="isTheme">
+          <!-- これは HXT が對應してゐない。
+               <data type="string">
+               <param name="pattern">yes|no</param>
+               </data>
+          -->
+          <text />
+        </attribute>
+      </optional>
+
+      <optional>
+        <!-- text/x-rakka でなければ無視される -->
+        <attribute name="isFeed">
+          <!-- yes/no -->
+          <text />
+        </attribute>
+      </optional>
+
+      <optional>
+        <!-- ログインしてゐないユーザーの編集を禁止するフラグ -->
+        <attribute name="isLocked">
+          <!-- yes/no -->
+          <text />
+        </attribute>
+      </optional>
+
+      <optional>
+        <!-- 更新履歴や RSS から削除されるフラグ -->
+        <attribute name="isBoring">
+          <!-- yes/no -->
+          <text />
+        </attribute>
+      </optional>
+
+      <interleave>
+        <optional>
+          <element name="summary">
+            <text />
+          </element>
+        </optional>
+
+        <optional>
+          <element name="otherLang">
+            <zeroOrMore>
+              <element name="link">
+                <attribute name="lang">
+                  <!-- RFC 4646 言語コード -->
+                  <text />
+                </attribute>
+                <attribute name="page">
+                  <!-- ページ名 -->
+                  <data type="anyURI" />
+                </attribute>
+              </element>
+            </zeroOrMore>
+          </element>
+        </optional>
+      </interleave>
+
+      <choice>
+        <element name="textData">
+          <text />
+        </element>
 
-    <element name="binaryData">
-      <text />
-    </element>
+        <element name="binaryData">
+          <!-- これは HXT が對應してゐない。
+               <data type="base64Binary" />
+          -->
+          <text />
+        </element>
+      </choice>
+    </group>
   </choice>
 </element>