+ loadPageRedirect
+ = do hist <- getNodeHistory True path
+ content <- getFileContents path
+
+ let pageRev = fst $ head hist
+ dest = chomp $ decodeString content
+
+ lastMod <- getRevisionProp "svn:date"
+ >>= return . fromJust . parseW3CDateTime . chomp . fromJust
+
+ return Redirection {
+ redirName = name
+ , redirDest = dest
+ , redirRevision = pageRev
+ , redirLastMod = zonedTimeToUTC lastMod
+ , redirUpdateInfo = undefined
+ }
+
+
+putPageIntoRepository :: Repository -> Page -> IO StatusCode
+putPageIntoRepository repos page
+ = filterSvnError $
+ do let name = pageName page
+ ret <- case pageUpdateInfo page of
+ Just ui
+ -> doReposTxn
+ repos
+ (uiOldRevision ui)
+ "[Rakka]"
+ (Just "Automatic commit by Rakka for page update")
+ $ do case uiOldName ui of
+ Nothing -> return ()
+ Just oldName -> renamePage (uiOldRevision ui) oldName name
+ updatePage name
+ Nothing
+ -> do fs <- getRepositoryFS repos
+ rev <- getYoungestRev fs
+ doReposTxn repos
+ rev
+ "[Rakka]"
+ (Just "Automatic commit by Rakka for page creation")
+ $ do createPage name
+ updatePage name
+ case ret of
+ Left _ -> return Conflict
+ Right _ -> return Created
+ where
+ renamePage :: RevNum -> PageName -> PageName -> Txn ()
+ renamePage oldRev oldName newName
+ = do let oldPath = mkPagePath oldName
+ newPath = mkPagePath newName
+ createParentDirectories newPath
+ copyEntry oldRev oldPath newPath
+ deleteEntry oldPath
+ deleteEmptyParentDirectories oldPath
+
+ createPage :: PageName -> Txn ()
+ createPage name
+ = do let path = mkPagePath name
+ createParentDirectories path
+ makeFile path
+
+ updatePage :: PageName -> Txn ()
+ updatePage name
+ | isRedirect page = updatePageRedirect name
+ | isEntity page = updatePageEntity name
+ | otherwise = fail "neither redirection nor page"
+
+ updatePageRedirect :: PageName -> Txn ()
+ updatePageRedirect name
+ = do let path = mkPagePath name
+ setNodeProp path "svn:mime-type" (Just "application/x-rakka-redirection")
+ setNodeProp path "rakka:lang" Nothing
+ setNodeProp path "rakka:isTheme" Nothing
+ setNodeProp path "rakka:isFeed" Nothing
+ setNodeProp path "rakka:isLocked" Nothing
+ setNodeProp path "rakka:isBinary" Nothing
+ setNodeProp path "rakka:summary" Nothing
+ setNodeProp path "rakka:otherLang" Nothing
+ applyText path Nothing (encodeString (redirDest page) ++ "\n")
+
+ updatePageEntity :: PageName -> Txn ()
+ updatePageEntity name
+ = do let path = mkPagePath name
+ setNodeProp path "svn:mime-type" ((Just . show . entityType) page)
+ setNodeProp path "rakka:lang" (entityLanguage page)
+ setNodeProp path "rakka:isTheme" (encodeFlag $ entityIsTheme page)
+ setNodeProp path "rakka:isFeed" (encodeFlag $ entityIsFeed page)
+ setNodeProp path "rakka:isLocked" (encodeFlag $ entityIsLocked page)
+ setNodeProp path "rakka:isBinary" (encodeFlag $ entityIsBinary page)
+ setNodeProp path "rakka:summary" (entitySummary page)
+ setNodeProp path "rakka:otherLang" (let otherLang = entityOtherLang page
+ in
+ if M.null otherLang then
+ Nothing
+ else
+ Just (serializeStringPairs $ M.toList otherLang))
+ applyTextLBS path Nothing (entityContent page)
+
+ encodeFlag :: Bool -> Maybe String
+ encodeFlag True = Just "*"
+ encodeFlag False = Nothing
+
+
+createParentDirectories :: FilePath -> Txn ()
+createParentDirectories path
+ = do let parentPath = takeDirectory path
+ kind <- checkPath parentPath
+ case kind of
+ NoNode -> do createParentDirectories parentPath
+ makeDirectory parentPath
+ FileNode -> fail ("createParentDirectories: already exists a file: " ++ parentPath)
+ DirNode -> return ()
+
+
+deletePageFromRepository :: Repository -> PageName -> IO StatusCode
+deletePageFromRepository repos name
+ = filterSvnError $
+ do let path = mkPagePath name
+ fs <- getRepositoryFS repos
+ rev <- getYoungestRev fs
+ exists <- withRevision fs rev $ isFile path
+ if exists then
+ do doReposTxn repos
+ rev
+ "[Rakka]"
+ (Just "Automatic commit by Rakka for page deleting")
+ $ do deleteEntry path
+ deleteEmptyParentDirectories path
+ return NoContent
+ else
+ return NotFound
+
+
+deleteEmptyParentDirectories :: FilePath -> Txn ()
+deleteEmptyParentDirectories path
+ = do let parentPath = takeDirectory path
+ contents <- getDirEntries parentPath
+ when (null contents)
+ $ do deleteEntry parentPath
+ deleteEmptyParentDirectories parentPath
+
+
+filterSvnError :: IO a -> IO a
+filterSvnError f = catchDyn f rethrow
+ where
+ rethrow :: SvnError -> IO a
+ rethrow err
+ = let code = svnErrCode err
+ msg = svnErrMsg err
+ in
+ fail $ "SvnError: " ++ (show code) ++ ": " ++ msg