Experimental changes to Vervis.
Clone
HTTPS:
darcs clone https://vervis.peers.community/repos/KrXYo
SSH:
darcs clone USERNAME@vervis.peers.community:KrXYo
Tags
TODO
Repo.hs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | {- 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.Field.Repo
( mkIdentField
, selectCollabFromAll
, selectCollabFromProject
, selectProjectForNew
, selectProjectForExisting
)
where
import Vervis.Import hiding ((==.), on, isNothing)
import Data.Char (isDigit)
import Data.Char.Local (isAsciiLetter)
import Data.Text (split)
import Database.Esqueleto
import qualified Database.Persist as P ((==.))
import Vervis.Model.Ident (shr2text, text2rp, prj2text)
checkIdentTemplate :: Field Handler Text -> Field Handler Text
checkIdentTemplate =
let charOk c = isAsciiLetter c || isDigit c
wordOk w = (not . null) w && all charOk w
identOk t = (not . null) t && all wordOk (split (== '-') t)
msg :: Text
msg = "The repo identifier must be a sequence of one or more words \
\separated by hyphens (‘-’), and each such word may contain \
\ASCII letters and digits."
in checkBool identOk msg
-- | Make sure the sharer doesn't already have a repo by the same name.
checkIdentUnique :: SharerId -> Field Handler Text -> Field Handler Text
checkIdentUnique sid = checkM $ \ ident -> do
let ident' = text2rp ident
sames <- runDB $ select $ from $ \ repo -> do
where_ $
repo ^. RepoSharer ==. val sid &&.
lower_ (repo ^. RepoIdent) ==. lower_ (val ident')
limit 1
return ()
return $ if null sames
then Right ident
else Left ("You already have a repo by that name" :: Text)
mkIdentField :: SharerId -> Field Handler Text
mkIdentField sid = checkIdentUnique sid . checkIdentTemplate $ textField
-- | Select a new collaborator for a repo, from the list of users of the
-- server. It can be any person who isn't already a collaborator.
selectCollabFromAll :: RepoId -> Field Handler PersonId
selectCollabFromAll rid = selectField $ do
l <- runDB $ select $
from $ \ (collab `RightOuterJoin` person `InnerJoin` sharer) -> do
on $ person ^. PersonIdent ==. sharer ^. SharerId
on $
collab ?. RepoCollabRepo ==. just (val rid) &&.
collab ?. RepoCollabPerson ==. just (person ^. PersonId)
where_ $ isNothing $ collab ?. RepoCollabId
return (sharer ^. SharerIdent, person ^. PersonId)
optionsPairs $ map (shr2text . unValue *** unValue) l
-- | Select a new collaborator for a repo, from the list of collaborators of
-- the project it belongs to. It can be any collaborator of the project, who
-- isn't yet a collaborator of the repo.
selectCollabFromProject :: ProjectId -> RepoId -> Field Handler PersonId
selectCollabFromProject jid rid = selectField $ do
l <- runDB $ select $ from $
\ ( pcollab `InnerJoin`
person `LeftOuterJoin`
rcollab `InnerJoin`
sharer
) -> do
on $ person ^. PersonIdent ==. sharer ^. SharerId
on $
rcollab ?. RepoCollabRepo ==. just (val rid) &&.
rcollab ?. RepoCollabPerson ==. just (person ^. PersonId)
on $
pcollab ^. ProjectCollabProject ==. val jid &&.
pcollab ^. ProjectCollabPerson ==. person ^. PersonId
where_ $ isNothing $ rcollab ?. RepoCollabId
return (sharer ^. SharerIdent, person ^. PersonId)
optionsPairs $ map (shr2text . unValue *** unValue) l
-- | Select a project for a new repository to belong to. It can be any project
-- of the same sharer who's sharing the repo.
selectProjectForNew :: SharerId -> Field Handler ProjectId
selectProjectForNew sid =
selectField $
optionsPersistKey [ProjectSharer P.==. sid] [] $
prj2text . projectIdent
-- | Select a project for a repository to belong to. It can be any project of
-- the same sharer who's sharing the repo.
--
-- However, there's an additional requirement that all repo collaborators are
-- also project collaborators. I'm not sure I want this requirement, but it's
-- easier to require it now and remove later, than require it later when the DB
-- is already full of live repos and projects.
--
-- Also, a repo that is the wiki of the project can't be moved, but this is NOT
-- CHECKED HERE. That's something to check before running the form, i.e. in the
-- handler itself.
selectProjectForExisting :: SharerId -> RepoId -> Field Handler ProjectId
selectProjectForExisting sid rid = checkMembers $ selectProjectForNew sid
where
checkMembers = checkM $ \ jid -> do
l <- runDB $ select $ from $ \ (rc `LeftOuterJoin` pc) -> do
on $
rc ^. RepoCollabRepo ==. val rid &&.
pc ?. ProjectCollabProject ==. just (val jid) &&.
pc ?. ProjectCollabPerson ==. just (rc ^. RepoCollabPerson)
where_ $ isNothing $ pc ?. ProjectCollabId
limit 1
return ()
return $ if null l
then Right jid
else Left ("Some repo members aren't project members" :: Text)
|