Initial minimal optional per-project wiki
This commit is contained in:
parent
ef810f2854
commit
f8e1442e72
10 changed files with 201 additions and 7 deletions
|
@ -103,6 +103,7 @@ Project
|
||||||
name Text Maybe
|
name Text Maybe
|
||||||
desc Text Maybe
|
desc Text Maybe
|
||||||
nextTicket Int default=1
|
nextTicket Int default=1
|
||||||
|
wiki RepoId Maybe
|
||||||
|
|
||||||
UniqueProject ident sharer
|
UniqueProject ident sharer
|
||||||
|
|
||||||
|
|
|
@ -99,5 +99,4 @@
|
||||||
/s/#ShrIdent/p/#PrjIdent/t/#Int/d/!reply TicketTopReplyR GET
|
/s/#ShrIdent/p/#PrjIdent/t/#Int/d/!reply TicketTopReplyR GET
|
||||||
/s/#ShrIdent/p/#PrjIdent/t/#Int/d/#Int/reply TicketReplyR GET
|
/s/#ShrIdent/p/#PrjIdent/t/#Int/d/#Int/reply TicketReplyR GET
|
||||||
|
|
||||||
-- /s/#ShrIdent/p/#PrjIdent/w WikiR GET
|
/s/#ShrIdent/p/#PrjIdent/w/+Texts WikiPageR GET
|
||||||
-- /s/#ShrIdent/p/#PrjIdent/w/+Texts WikiPageR GET
|
|
||||||
|
|
|
@ -62,6 +62,7 @@ import Vervis.Handler.Repo
|
||||||
import Vervis.Handler.Role
|
import Vervis.Handler.Role
|
||||||
import Vervis.Handler.Sharer
|
import Vervis.Handler.Sharer
|
||||||
import Vervis.Handler.Ticket
|
import Vervis.Handler.Ticket
|
||||||
|
import Vervis.Handler.Wiki
|
||||||
|
|
||||||
import Vervis.Ssh (runSsh)
|
import Vervis.Ssh (runSsh)
|
||||||
|
|
||||||
|
|
|
@ -15,14 +15,18 @@
|
||||||
|
|
||||||
module Vervis.Darcs
|
module Vervis.Darcs
|
||||||
( readSourceView
|
( readSourceView
|
||||||
|
, readWikiView
|
||||||
, readChangesView
|
, readChangesView
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
import Prelude
|
import Prelude hiding (lookup)
|
||||||
|
|
||||||
|
import Control.Applicative ((<|>))
|
||||||
import Control.Monad.Trans.Class (lift)
|
import Control.Monad.Trans.Class (lift)
|
||||||
import Control.Monad.Trans.Except (ExceptT (ExceptT), runExceptT)
|
import Control.Monad.Trans.Except (ExceptT (ExceptT), runExceptT)
|
||||||
|
import Data.Bool (bool)
|
||||||
|
import Data.Maybe (listToMaybe, mapMaybe)
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
import Data.Text.Encoding (encodeUtf8, decodeUtf8With)
|
import Data.Text.Encoding (encodeUtf8, decodeUtf8With)
|
||||||
import Data.Text.Encoding.Error (strictDecode)
|
import Data.Text.Encoding.Error (strictDecode)
|
||||||
|
@ -52,6 +56,7 @@ import Vervis.Changes
|
||||||
import Vervis.Foundation (Widget)
|
import Vervis.Foundation (Widget)
|
||||||
import Vervis.Readme
|
import Vervis.Readme
|
||||||
import Vervis.SourceTree
|
import Vervis.SourceTree
|
||||||
|
import Vervis.Wiki (WikiView (..))
|
||||||
|
|
||||||
dirToAnchoredPath :: [EntryName] -> AnchoredPath
|
dirToAnchoredPath :: [EntryName] -> AnchoredPath
|
||||||
dirToAnchoredPath = AnchoredPath . map (Name . encodeUtf8)
|
dirToAnchoredPath = AnchoredPath . map (Name . encodeUtf8)
|
||||||
|
@ -91,6 +96,13 @@ itemToSourceView name (SubTree tree) = do
|
||||||
}
|
}
|
||||||
itemToSourceView _name (Stub _load _hash) = error "supposed to be expanded"
|
itemToSourceView _name (Stub _load _hash) = error "supposed to be expanded"
|
||||||
|
|
||||||
|
readStubbedTree :: FilePath -> IO (Tree IO)
|
||||||
|
readStubbedTree path = do
|
||||||
|
let darcsDir = path </> "_darcs"
|
||||||
|
(msize, hash) <- readPristineRoot darcsDir
|
||||||
|
let pristineDir = darcsDir </> "pristine.hashed"
|
||||||
|
readDarcsHashed pristineDir (msize, hash)
|
||||||
|
|
||||||
readSourceView
|
readSourceView
|
||||||
:: FilePath
|
:: FilePath
|
||||||
-- ^ Repository path
|
-- ^ Repository path
|
||||||
|
@ -98,10 +110,7 @@ readSourceView
|
||||||
-- ^ Path in the source tree pointing to a file or directory
|
-- ^ Path in the source tree pointing to a file or directory
|
||||||
-> IO (Maybe (SourceView Widget))
|
-> IO (Maybe (SourceView Widget))
|
||||||
readSourceView path dir = do
|
readSourceView path dir = do
|
||||||
let darcsDir = path </> "_darcs"
|
stubbedTree <- readStubbedTree path
|
||||||
(msize, hash) <- readPristineRoot darcsDir
|
|
||||||
let pristineDir = darcsDir </> "pristine.hashed"
|
|
||||||
stubbedTree <- readDarcsHashed pristineDir (msize, hash)
|
|
||||||
msv <- if null dir
|
msv <- if null dir
|
||||||
then do
|
then do
|
||||||
let items = listImmediate stubbedTree
|
let items = listImmediate stubbedTree
|
||||||
|
@ -118,6 +127,56 @@ readSourceView path dir = do
|
||||||
for mitem $ itemToSourceView (last dir)
|
for mitem $ itemToSourceView (last dir)
|
||||||
return $ renderSources dir <$> msv
|
return $ renderSources dir <$> msv
|
||||||
|
|
||||||
|
readWikiView
|
||||||
|
:: (EntryName -> EntryName -> Maybe Text)
|
||||||
|
-- ^ Page name predicate. Returns 'Nothing' for a file which isn't a page.
|
||||||
|
-- For a page file, returns 'Just' the page name, which is the filename
|
||||||
|
-- with some parts possibly removed or added. For example, you may wish to
|
||||||
|
-- remove any extensions, replace underscores with spaces and so on.
|
||||||
|
-> (EntryName -> Bool)
|
||||||
|
-- ^ Main page predicate. This is used to pick a top-level page to display
|
||||||
|
-- as the wiki root page.
|
||||||
|
-> FilePath
|
||||||
|
-- ^ Repository path.
|
||||||
|
-> [EntryName]
|
||||||
|
-- ^ Path in the source tree pointing to a file. The last component doesn't
|
||||||
|
-- have to be the full name of the file though, but it much match the page
|
||||||
|
-- predicate for the actual file to be found.
|
||||||
|
-> IO (Maybe WikiView)
|
||||||
|
readWikiView isPage isMain path dir = do
|
||||||
|
stubbedTree <- readStubbedTree path
|
||||||
|
let (parent, ispage, mfile) =
|
||||||
|
if null dir
|
||||||
|
then
|
||||||
|
( []
|
||||||
|
, bool Nothing (Just Nothing) . isMain
|
||||||
|
, Nothing
|
||||||
|
)
|
||||||
|
else
|
||||||
|
( init dir
|
||||||
|
, maybe Nothing (Just . Just) . isPage lst
|
||||||
|
, Just $ Name $ encodeUtf8 lst
|
||||||
|
)
|
||||||
|
where
|
||||||
|
lst = last dir
|
||||||
|
anch = dirToAnchoredPath parent
|
||||||
|
matchBlob f (n, (File (Blob load _))) = f (nameToText n) load
|
||||||
|
matchBlob _ _ = Nothing
|
||||||
|
matchBlob' f (File (Blob load _)) = Just $ f load
|
||||||
|
matchBlob' _ _ = Nothing
|
||||||
|
page name load = (,) load . Just <$> ispage name
|
||||||
|
matchP = listToMaybe . mapMaybe (matchBlob page) . listImmediate
|
||||||
|
matchF t = mfile >>= lookup t >>= matchBlob' (flip (,) Nothing)
|
||||||
|
expandedTree <- expandPath stubbedTree anch
|
||||||
|
let mpage = case find expandedTree anch of
|
||||||
|
Nothing -> Nothing
|
||||||
|
Just (File _) -> Nothing
|
||||||
|
Just (Stub _ _) -> error "supposed to be expanded"
|
||||||
|
Just (SubTree tree) -> matchP tree <|> matchF tree
|
||||||
|
mkview Nothing b = WikiViewRaw b
|
||||||
|
mkview (Just mt) b = WikiViewPage mt b
|
||||||
|
for mpage $ \ (load, mmtitle) -> mkview mmtitle <$> load
|
||||||
|
|
||||||
readChangesView
|
readChangesView
|
||||||
:: FilePath
|
:: FilePath
|
||||||
-- ^ Repository path
|
-- ^ Repository path
|
||||||
|
|
|
@ -38,6 +38,7 @@ newProjectAForm sid = Project
|
||||||
<*> aopt textField "Name" Nothing
|
<*> aopt textField "Name" Nothing
|
||||||
<*> aopt textField "Description" Nothing
|
<*> aopt textField "Description" Nothing
|
||||||
<*> pure 1
|
<*> pure 1
|
||||||
|
<*> pure Nothing
|
||||||
|
|
||||||
newProjectForm :: SharerId -> Form Project
|
newProjectForm :: SharerId -> Form Project
|
||||||
newProjectForm = renderDivs . newProjectAForm
|
newProjectForm = renderDivs . newProjectAForm
|
||||||
|
|
|
@ -391,3 +391,5 @@ instance YesodBreadcrumbs App where
|
||||||
, Just $
|
, Just $
|
||||||
TicketMessageR shar proj num cnum
|
TicketMessageR shar proj num cnum
|
||||||
)
|
)
|
||||||
|
|
||||||
|
WikiPageR shr prj _page -> ("Wiki", Just $ ProjectR shr prj)
|
||||||
|
|
76
src/Vervis/Handler/Wiki.hs
Normal file
76
src/Vervis/Handler/Wiki.hs
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
{- This file is part of Vervis.
|
||||||
|
-
|
||||||
|
- Written in 2016 by fr33domlover <fr33domlover@riseup.net>.
|
||||||
|
-
|
||||||
|
- ♡ Copying is an act of love. Please copy, reuse and share.
|
||||||
|
-
|
||||||
|
- The author(s) have dedicated all copyright and related and neighboring
|
||||||
|
- rights to this software to the public domain worldwide. This software is
|
||||||
|
- distributed without any warranty.
|
||||||
|
-
|
||||||
|
- You should have received a copy of the CC0 Public Domain Dedication along
|
||||||
|
- with this software. If not, see
|
||||||
|
- <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
|
-}
|
||||||
|
|
||||||
|
module Vervis.Handler.Wiki
|
||||||
|
( getWikiPageR
|
||||||
|
)
|
||||||
|
where
|
||||||
|
|
||||||
|
import Prelude
|
||||||
|
|
||||||
|
import Control.Monad.IO.Class (liftIO)
|
||||||
|
import Data.Text (Text)
|
||||||
|
import Data.Traversable (for)
|
||||||
|
import Database.Persist (Entity (..), getJust)
|
||||||
|
import Text.Blaze.Html (Html)
|
||||||
|
import Yesod.Core (defaultLayout)
|
||||||
|
import Yesod.Core.Content (toContent, typeOctet)
|
||||||
|
import Yesod.Core.Handler (setMessage, redirect, notFound, sendResponse)
|
||||||
|
import Yesod.Persist.Core (runDB, getBy404)
|
||||||
|
|
||||||
|
import Text.FilePath.Local (breakExt)
|
||||||
|
import Vervis.Darcs
|
||||||
|
import Vervis.Foundation
|
||||||
|
import Vervis.MediaType
|
||||||
|
import Vervis.Model
|
||||||
|
import Vervis.Model.Ident
|
||||||
|
import Vervis.Model.Repo
|
||||||
|
import Vervis.Path (askRepoDir)
|
||||||
|
import Vervis.Render (renderSourceBL)
|
||||||
|
import Vervis.Settings (widgetFile)
|
||||||
|
import Vervis.Wiki
|
||||||
|
|
||||||
|
getWikiPageR :: ShrIdent -> PrjIdent -> [Text] -> Handler Html
|
||||||
|
getWikiPageR shr prj path = do
|
||||||
|
m <- runDB $ do
|
||||||
|
Entity sid _ <- getBy404 $ UniqueSharer shr
|
||||||
|
Entity _ j <- getBy404 $ UniqueProject prj sid
|
||||||
|
for (projectWiki j) $ \ rid -> do
|
||||||
|
r <- getJust rid
|
||||||
|
s <- getJust $ repoSharer r
|
||||||
|
return (sharerIdent s, repoIdent r, repoVcs r)
|
||||||
|
case m of
|
||||||
|
Nothing -> do
|
||||||
|
setMessage "This project doesn’t have a wiki."
|
||||||
|
redirect $ ProjectR shr prj
|
||||||
|
Just (s, r, v) -> do
|
||||||
|
root <- askRepoDir s r
|
||||||
|
case v of
|
||||||
|
VCSDarcs -> do
|
||||||
|
let ispage name file =
|
||||||
|
let (b, e) = breakExt file
|
||||||
|
in if e == "md" && b == name
|
||||||
|
then Just b
|
||||||
|
else Nothing
|
||||||
|
ismain = (== "README.md")
|
||||||
|
mwv <- liftIO $ readWikiView ispage ismain root path
|
||||||
|
case mwv of
|
||||||
|
Nothing -> notFound
|
||||||
|
Just (WikiViewRaw b) ->
|
||||||
|
sendResponse (typeOctet, toContent b)
|
||||||
|
Just (WikiViewPage mt b) -> do
|
||||||
|
let page = renderSourceBL Markdown b
|
||||||
|
defaultLayout $(widgetFile "wiki")
|
||||||
|
VCSGit -> error "Not implemented yet"
|
35
src/Vervis/Wiki.hs
Normal file
35
src/Vervis/Wiki.hs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
{- This file is part of Vervis.
|
||||||
|
-
|
||||||
|
- Written in 2016 by fr33domlover <fr33domlover@riseup.net>.
|
||||||
|
-
|
||||||
|
- ♡ Copying is an act of love. Please copy, reuse and share.
|
||||||
|
-
|
||||||
|
- The author(s) have dedicated all copyright and related and neighboring
|
||||||
|
- rights to this software to the public domain worldwide. This software is
|
||||||
|
- distributed without any warranty.
|
||||||
|
-
|
||||||
|
- You should have received a copy of the CC0 Public Domain Dedication along
|
||||||
|
- with this software. If not, see
|
||||||
|
- <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
|
-}
|
||||||
|
|
||||||
|
module Vervis.Wiki
|
||||||
|
( WikiView (..)
|
||||||
|
)
|
||||||
|
where
|
||||||
|
|
||||||
|
import Prelude
|
||||||
|
|
||||||
|
import Data.Text (Text)
|
||||||
|
|
||||||
|
import qualified Data.ByteString.Lazy as BL (ByteString)
|
||||||
|
|
||||||
|
import Text.FilePath.Local (breakExt)
|
||||||
|
import Vervis.Foundation (Widget)
|
||||||
|
import Vervis.MediaType (chooseMediaType)
|
||||||
|
import Vervis.Readme (renderReadme)
|
||||||
|
import Vervis.Render (renderSourceBL)
|
||||||
|
|
||||||
|
data WikiView
|
||||||
|
= WikiViewPage (Maybe Text) BL.ByteString
|
||||||
|
| WikiViewRaw BL.ByteString
|
18
templates/wiki.hamlet
Normal file
18
templates/wiki.hamlet
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
$# This file is part of Vervis.
|
||||||
|
$#
|
||||||
|
$# Written in 2016 by fr33domlover <fr33domlover@riseup.net>.
|
||||||
|
$#
|
||||||
|
$# ♡ Copying is an act of love. Please copy, reuse and share.
|
||||||
|
$#
|
||||||
|
$# The author(s) have dedicated all copyright and related and neighboring
|
||||||
|
$# rights to this software to the public domain worldwide. This software is
|
||||||
|
$# distributed without any warranty.
|
||||||
|
$#
|
||||||
|
$# You should have received a copy of the CC0 Public Domain Dedication along
|
||||||
|
$# with this software. If not, see
|
||||||
|
$# <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
|
|
||||||
|
$maybe title <- mt
|
||||||
|
<h2>#{title}
|
||||||
|
|
||||||
|
^{page}
|
|
@ -113,6 +113,7 @@ library
|
||||||
Vervis.Handler.Role
|
Vervis.Handler.Role
|
||||||
Vervis.Handler.Sharer
|
Vervis.Handler.Sharer
|
||||||
Vervis.Handler.Ticket
|
Vervis.Handler.Ticket
|
||||||
|
Vervis.Handler.Wiki
|
||||||
Vervis.Import
|
Vervis.Import
|
||||||
Vervis.Import.NoFoundation
|
Vervis.Import.NoFoundation
|
||||||
Vervis.MediaType
|
Vervis.MediaType
|
||||||
|
@ -136,6 +137,7 @@ library
|
||||||
Vervis.Widget.Discussion
|
Vervis.Widget.Discussion
|
||||||
Vervis.Widget.Repo
|
Vervis.Widget.Repo
|
||||||
Vervis.Widget.Sharer
|
Vervis.Widget.Sharer
|
||||||
|
Vervis.Wiki
|
||||||
-- other-modules:
|
-- other-modules:
|
||||||
default-extensions: TemplateHaskell
|
default-extensions: TemplateHaskell
|
||||||
QuasiQuotes
|
QuasiQuotes
|
||||||
|
|
Loading…
Reference in a new issue