2016-02-17 21:53:53 +00:00
|
|
|
{- This file is part of Vervis.
|
|
|
|
-
|
2019-01-28 14:43:07 +00:00
|
|
|
- Written in 2016, 2019 by fr33domlover <fr33domlover@riseup.net>.
|
2016-02-17 21:53:53 +00:00
|
|
|
-
|
|
|
|
- ♡ 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/>.
|
|
|
|
-}
|
|
|
|
|
2016-02-23 08:45:03 +00:00
|
|
|
module Vervis.Handler.Project
|
2016-02-17 21:53:53 +00:00
|
|
|
( getProjectsR
|
2016-02-25 03:10:30 +00:00
|
|
|
, postProjectsR
|
|
|
|
, getProjectNewR
|
2016-02-17 21:53:53 +00:00
|
|
|
, getProjectR
|
2016-06-05 10:43:28 +00:00
|
|
|
, putProjectR
|
|
|
|
, postProjectR
|
|
|
|
, getProjectEditR
|
2016-06-01 08:52:14 +00:00
|
|
|
, getProjectDevsR
|
|
|
|
, postProjectDevsR
|
|
|
|
, getProjectDevNewR
|
|
|
|
, getProjectDevR
|
|
|
|
, deleteProjectDevR
|
|
|
|
, postProjectDevR
|
2019-06-11 12:19:51 +00:00
|
|
|
, getProjectTeamR
|
|
|
|
, getProjectFollowersR
|
2016-02-17 21:53:53 +00:00
|
|
|
)
|
|
|
|
where
|
|
|
|
|
2016-05-14 11:36:45 +00:00
|
|
|
import Data.Maybe (fromMaybe)
|
|
|
|
import Data.Text (Text)
|
|
|
|
import Database.Persist
|
2016-06-01 08:52:14 +00:00
|
|
|
import Database.Esqueleto hiding (delete, (%), (==.))
|
2016-05-14 11:36:45 +00:00
|
|
|
import Text.Blaze.Html (Html)
|
2016-06-06 06:48:59 +00:00
|
|
|
import Yesod.Auth (requireAuthId)
|
2019-03-20 12:01:10 +00:00
|
|
|
import Yesod.Core
|
2016-06-01 08:52:14 +00:00
|
|
|
import Yesod.Core.Handler (redirect, setMessage, lookupPostParam, notFound)
|
2016-05-14 11:36:45 +00:00
|
|
|
import Yesod.Form.Functions (runFormPost)
|
|
|
|
import Yesod.Form.Types (FormResult (..))
|
2016-08-08 19:05:22 +00:00
|
|
|
import Yesod.Persist.Core (runDB, get404, getBy404)
|
2016-05-14 11:36:45 +00:00
|
|
|
|
|
|
|
import qualified Database.Esqueleto as E
|
2016-02-17 21:53:53 +00:00
|
|
|
|
2019-03-20 12:01:10 +00:00
|
|
|
import Network.FedURI
|
2019-06-11 12:19:51 +00:00
|
|
|
import Web.ActivityPub hiding (Project (..))
|
|
|
|
import Yesod.ActivityPub
|
2019-03-20 12:01:10 +00:00
|
|
|
import Yesod.FedURI
|
|
|
|
|
2019-06-11 12:19:51 +00:00
|
|
|
import qualified Web.ActivityPub as AP
|
|
|
|
|
|
|
|
import Data.Either.Local
|
|
|
|
import Database.Persist.Local
|
|
|
|
import Yesod.Persist.Local
|
|
|
|
|
2019-06-15 04:39:13 +00:00
|
|
|
import Vervis.API
|
2019-06-11 12:19:51 +00:00
|
|
|
import Vervis.Federation
|
2016-02-25 03:10:30 +00:00
|
|
|
import Vervis.Form.Project
|
2016-05-14 11:36:45 +00:00
|
|
|
import Vervis.Foundation
|
|
|
|
import Vervis.Model
|
2016-05-23 20:46:54 +00:00
|
|
|
import Vervis.Model.Ident
|
2016-05-14 11:36:45 +00:00
|
|
|
import Vervis.Model.Repo
|
|
|
|
import Vervis.Settings
|
2016-06-01 08:52:14 +00:00
|
|
|
import Vervis.Widget.Sharer
|
2016-08-08 19:05:22 +00:00
|
|
|
import Vervis.Widget.Workflow
|
2016-02-17 21:53:53 +00:00
|
|
|
|
2016-05-23 12:24:14 +00:00
|
|
|
getProjectsR :: ShrIdent -> Handler Html
|
2016-02-17 21:53:53 +00:00
|
|
|
getProjectsR ident = do
|
2016-06-01 08:52:14 +00:00
|
|
|
projects <- runDB $ select $ from $ \ (sharer, project) -> do
|
|
|
|
where_ $
|
|
|
|
sharer ^. SharerIdent E.==. val ident &&.
|
|
|
|
sharer ^. SharerId E.==. project ^. ProjectSharer
|
|
|
|
orderBy [asc $ project ^. ProjectIdent]
|
|
|
|
return $ project ^. ProjectIdent
|
2016-05-13 22:11:46 +00:00
|
|
|
defaultLayout $(widgetFile "project/list")
|
2016-02-17 21:53:53 +00:00
|
|
|
|
2016-05-23 12:24:14 +00:00
|
|
|
postProjectsR :: ShrIdent -> Handler Html
|
2016-06-06 06:48:59 +00:00
|
|
|
postProjectsR shr = do
|
|
|
|
Entity sid _ <- runDB $ getBy404 $ UniqueSharer shr
|
2016-06-06 17:29:54 +00:00
|
|
|
((result, widget), enctype) <- runFormPost $ newProjectForm sid
|
2016-02-25 03:10:30 +00:00
|
|
|
case result of
|
2016-06-06 06:48:59 +00:00
|
|
|
FormSuccess np -> do
|
2016-06-06 17:29:54 +00:00
|
|
|
pid <- requireAuthId
|
2016-06-06 06:48:59 +00:00
|
|
|
runDB $ do
|
2019-06-09 16:21:23 +00:00
|
|
|
ibid <- insert Inbox
|
2019-06-16 21:34:06 +00:00
|
|
|
obid <- insert Outbox
|
2019-06-11 12:19:51 +00:00
|
|
|
fsid <- insert FollowerSet
|
2016-06-06 06:48:59 +00:00
|
|
|
let project = Project
|
|
|
|
{ projectIdent = npIdent np
|
|
|
|
, projectSharer = sid
|
|
|
|
, projectName = npName np
|
|
|
|
, projectDesc = npDesc np
|
2016-08-08 19:05:22 +00:00
|
|
|
, projectWorkflow = npWflow np
|
2016-06-06 06:48:59 +00:00
|
|
|
, projectNextTicket = 1
|
|
|
|
, projectWiki = Nothing
|
2019-01-29 22:24:32 +00:00
|
|
|
, projectCollabAnon = Nothing
|
|
|
|
, projectCollabUser = Nothing
|
2019-06-09 16:21:23 +00:00
|
|
|
, projectInbox = ibid
|
2019-06-16 21:34:06 +00:00
|
|
|
, projectOutbox = obid
|
2019-06-11 12:19:51 +00:00
|
|
|
, projectFollowers = fsid
|
2016-06-06 06:48:59 +00:00
|
|
|
}
|
|
|
|
jid <- insert project
|
|
|
|
let collab = ProjectCollab
|
|
|
|
{ projectCollabProject = jid
|
|
|
|
, projectCollabPerson = pid
|
|
|
|
, projectCollabRole = npRole np
|
|
|
|
}
|
|
|
|
insert_ collab
|
2016-02-25 03:10:30 +00:00
|
|
|
setMessage "Project added."
|
2016-06-06 06:48:59 +00:00
|
|
|
redirect $ ProjectR shr (npIdent np)
|
2016-02-25 03:10:30 +00:00
|
|
|
FormMissing -> do
|
|
|
|
setMessage "Field(s) missing"
|
2016-05-13 22:11:46 +00:00
|
|
|
defaultLayout $(widgetFile "project/new")
|
2016-05-13 22:07:56 +00:00
|
|
|
FormFailure _l -> do
|
2016-05-13 22:06:23 +00:00
|
|
|
setMessage "Project creation failed, see below"
|
2016-05-13 22:11:46 +00:00
|
|
|
defaultLayout $(widgetFile "project/new")
|
2016-02-25 03:10:30 +00:00
|
|
|
|
2016-05-23 12:24:14 +00:00
|
|
|
getProjectNewR :: ShrIdent -> Handler Html
|
2016-06-06 06:48:59 +00:00
|
|
|
getProjectNewR shr = do
|
|
|
|
Entity sid _ <- runDB $ getBy404 $ UniqueSharer shr
|
2016-06-06 17:29:54 +00:00
|
|
|
((_result, widget), enctype) <- runFormPost $ newProjectForm sid
|
2016-05-13 22:11:46 +00:00
|
|
|
defaultLayout $(widgetFile "project/new")
|
2016-02-25 03:10:30 +00:00
|
|
|
|
2019-03-20 12:01:10 +00:00
|
|
|
getProjectR :: ShrIdent -> PrjIdent -> Handler TypedContent
|
2019-06-11 12:19:51 +00:00
|
|
|
getProjectR shar proj = do
|
|
|
|
(project, workflow, wsharer, repos) <- runDB $ do
|
|
|
|
Entity sid s <- getBy404 $ UniqueSharer shar
|
|
|
|
Entity pid p <- getBy404 $ UniqueProject proj sid
|
|
|
|
w <- get404 $ projectWorkflow p
|
|
|
|
sw <-
|
|
|
|
if workflowSharer w == sid
|
|
|
|
then return s
|
|
|
|
else get404 $ workflowSharer w
|
|
|
|
rs <- selectList [RepoProject ==. Just pid] [Asc RepoIdent]
|
|
|
|
return (p, w, sw, rs)
|
|
|
|
|
|
|
|
route2fed <- getEncodeRouteHome
|
|
|
|
route2local <- getEncodeRouteLocal
|
|
|
|
let projectAP = AP.Project
|
|
|
|
{ AP.projectActor = Actor
|
|
|
|
{ actorId = route2local $ ProjectR shar proj
|
|
|
|
, actorType = ActorTypeProject
|
|
|
|
, actorUsername = Nothing
|
|
|
|
, actorName =
|
|
|
|
Just $ fromMaybe (prj2text proj) $ projectName project
|
|
|
|
, actorSummary = projectDesc project
|
|
|
|
, actorInbox = route2local $ ProjectInboxR shar proj
|
2019-06-30 01:18:52 +00:00
|
|
|
, actorOutbox =
|
|
|
|
Just $ route2local $ ProjectOutboxR shar proj
|
2019-06-11 12:19:51 +00:00
|
|
|
, actorFollowers =
|
|
|
|
Just $ route2local $ ProjectFollowersR shar proj
|
|
|
|
, actorPublicKeys =
|
|
|
|
[ Left $ route2local ActorKey1R
|
|
|
|
, Left $ route2local ActorKey2R
|
|
|
|
]
|
|
|
|
}
|
|
|
|
, AP.projectTeam = route2local $ ProjectTeamR shar proj
|
2019-03-20 12:01:10 +00:00
|
|
|
}
|
2019-10-02 08:07:26 +00:00
|
|
|
followButton =
|
|
|
|
followW
|
|
|
|
(ProjectFollowR shar proj)
|
|
|
|
(return $ projectFollowers project)
|
2019-06-11 12:19:51 +00:00
|
|
|
provideHtmlAndAP projectAP $(widgetFile "project/one")
|
2016-06-01 08:52:14 +00:00
|
|
|
|
2016-06-05 10:43:28 +00:00
|
|
|
putProjectR :: ShrIdent -> PrjIdent -> Handler Html
|
|
|
|
putProjectR shr prj = do
|
2019-01-29 22:24:32 +00:00
|
|
|
(sid, ep@(Entity jid _)) <- runDB $ do
|
2016-06-05 10:43:28 +00:00
|
|
|
Entity sid _sharer <- getBy404 $ UniqueSharer shr
|
2019-01-29 22:24:32 +00:00
|
|
|
eproj <- getBy404 $ UniqueProject prj sid
|
|
|
|
return (sid, eproj)
|
|
|
|
((result, widget), enctype) <- runFormPost $ editProjectForm sid ep
|
2016-06-05 10:43:28 +00:00
|
|
|
case result of
|
|
|
|
FormSuccess project' -> do
|
|
|
|
runDB $ replace jid project'
|
|
|
|
setMessage "Project updated."
|
|
|
|
redirect $ ProjectR shr prj
|
|
|
|
FormMissing -> do
|
|
|
|
setMessage "Field(s) missing."
|
|
|
|
defaultLayout $(widgetFile "project/edit")
|
|
|
|
FormFailure _l -> do
|
|
|
|
setMessage "Project update failed, see errors below."
|
|
|
|
defaultLayout $(widgetFile "project/edit")
|
|
|
|
|
|
|
|
postProjectR :: ShrIdent -> PrjIdent -> Handler Html
|
|
|
|
postProjectR shr prj = do
|
|
|
|
mmethod <- lookupPostParam "_method"
|
|
|
|
case mmethod of
|
|
|
|
Just "PUT" -> putProjectR shr prj
|
|
|
|
_ -> notFound
|
|
|
|
|
|
|
|
getProjectEditR :: ShrIdent -> PrjIdent -> Handler Html
|
|
|
|
getProjectEditR shr prj = do
|
2019-01-29 22:24:32 +00:00
|
|
|
(sid, ep) <- runDB $ do
|
2016-06-05 10:43:28 +00:00
|
|
|
Entity sid _sharer <- getBy404 $ UniqueSharer shr
|
2019-01-29 22:24:32 +00:00
|
|
|
ep <- getBy404 $ UniqueProject prj sid
|
|
|
|
return (sid, ep)
|
|
|
|
((_result, widget), enctype) <- runFormPost $ editProjectForm sid ep
|
2016-06-05 10:43:28 +00:00
|
|
|
defaultLayout $(widgetFile "project/edit")
|
|
|
|
|
2016-06-01 08:52:14 +00:00
|
|
|
getProjectDevsR :: ShrIdent -> PrjIdent -> Handler Html
|
2019-01-28 14:43:07 +00:00
|
|
|
getProjectDevsR shr prj = do
|
2016-06-01 08:52:14 +00:00
|
|
|
devs <- runDB $ do
|
2019-01-28 14:43:07 +00:00
|
|
|
jid <- do
|
|
|
|
Entity sid _ <- getBy404 $ UniqueSharer shr
|
|
|
|
Entity jid _ <- getBy404 $ UniqueProject prj sid
|
|
|
|
return jid
|
|
|
|
select $ from $ \ (collab `InnerJoin`
|
|
|
|
person `InnerJoin`
|
|
|
|
sharer `LeftOuterJoin`
|
|
|
|
role) -> do
|
2019-05-31 15:02:57 +00:00
|
|
|
on $ collab ^. ProjectCollabRole E.==. role ?. RoleId
|
2019-01-28 14:43:07 +00:00
|
|
|
on $ person ^. PersonIdent E.==. sharer ^. SharerId
|
|
|
|
on $ collab ^. ProjectCollabPerson E.==. person ^. PersonId
|
|
|
|
where_ $ collab ^. ProjectCollabProject E.==. val jid
|
2019-05-31 15:02:57 +00:00
|
|
|
return (sharer, role ?. RoleIdent)
|
2016-06-01 08:52:14 +00:00
|
|
|
defaultLayout $(widgetFile "project/collab/list")
|
|
|
|
|
|
|
|
postProjectDevsR :: ShrIdent -> PrjIdent -> Handler Html
|
|
|
|
postProjectDevsR shr rp = do
|
2016-06-06 17:29:54 +00:00
|
|
|
(sid, jid) <- runDB $ do
|
2016-06-01 08:52:14 +00:00
|
|
|
Entity s _ <- getBy404 $ UniqueSharer shr
|
2016-06-06 17:29:54 +00:00
|
|
|
Entity j _ <- getBy404 $ UniqueProject rp s
|
|
|
|
return (s, j)
|
|
|
|
((result, widget), enctype) <- runFormPost $ newProjectCollabForm sid jid
|
2016-06-01 08:52:14 +00:00
|
|
|
case result of
|
|
|
|
FormSuccess nc -> do
|
|
|
|
runDB $ do
|
|
|
|
let collab = ProjectCollab
|
2016-06-06 17:29:54 +00:00
|
|
|
{ projectCollabProject = jid
|
2016-06-01 08:52:14 +00:00
|
|
|
, projectCollabPerson = ncPerson nc
|
|
|
|
, projectCollabRole = ncRole nc
|
|
|
|
}
|
|
|
|
insert_ collab
|
|
|
|
setMessage "Collaborator added."
|
|
|
|
redirect $ ProjectDevsR shr rp
|
|
|
|
FormMissing -> do
|
|
|
|
setMessage "Field(s) missing"
|
|
|
|
defaultLayout $(widgetFile "project/collab/new")
|
|
|
|
FormFailure _l -> do
|
|
|
|
setMessage "Operation failed, see errors below"
|
|
|
|
defaultLayout $(widgetFile "project/collab/new")
|
|
|
|
|
|
|
|
getProjectDevNewR :: ShrIdent -> PrjIdent -> Handler Html
|
|
|
|
getProjectDevNewR shr rp = do
|
2016-06-06 17:29:54 +00:00
|
|
|
(sid, jid) <- runDB $ do
|
2016-06-01 08:52:14 +00:00
|
|
|
Entity s _ <- getBy404 $ UniqueSharer shr
|
2016-06-06 17:29:54 +00:00
|
|
|
Entity j _ <- getBy404 $ UniqueProject rp s
|
|
|
|
return (s, j)
|
|
|
|
((_result, widget), enctype) <- runFormPost $ newProjectCollabForm sid jid
|
2016-06-01 08:52:14 +00:00
|
|
|
defaultLayout $(widgetFile "project/collab/new")
|
|
|
|
|
|
|
|
getProjectDevR :: ShrIdent -> PrjIdent -> ShrIdent -> Handler Html
|
2019-01-28 14:43:07 +00:00
|
|
|
getProjectDevR shr prj dev = do
|
|
|
|
mrl <- runDB $ do
|
2016-06-06 17:29:54 +00:00
|
|
|
jid <- do
|
2016-06-01 08:52:14 +00:00
|
|
|
Entity s _ <- getBy404 $ UniqueSharer shr
|
2019-01-28 14:43:07 +00:00
|
|
|
Entity j _ <- getBy404 $ UniqueProject prj s
|
2016-06-06 17:29:54 +00:00
|
|
|
return j
|
2016-06-01 08:52:14 +00:00
|
|
|
pid <- do
|
|
|
|
Entity s _ <- getBy404 $ UniqueSharer dev
|
|
|
|
Entity p _ <- getBy404 $ UniquePersonIdent s
|
|
|
|
return p
|
2016-06-06 17:29:54 +00:00
|
|
|
Entity _cid collab <- getBy404 $ UniqueProjectCollab jid pid
|
2019-05-31 15:02:57 +00:00
|
|
|
fmap roleIdent <$> traverse getJust (projectCollabRole collab)
|
2016-06-01 08:52:14 +00:00
|
|
|
defaultLayout $(widgetFile "project/collab/one")
|
|
|
|
|
|
|
|
deleteProjectDevR :: ShrIdent -> PrjIdent -> ShrIdent -> Handler Html
|
|
|
|
deleteProjectDevR shr rp dev = do
|
|
|
|
runDB $ do
|
2016-06-06 17:29:54 +00:00
|
|
|
jid <- do
|
2016-06-01 08:52:14 +00:00
|
|
|
Entity s _ <- getBy404 $ UniqueSharer shr
|
2016-06-06 17:29:54 +00:00
|
|
|
Entity j _ <- getBy404 $ UniqueProject rp s
|
|
|
|
return j
|
2016-06-01 08:52:14 +00:00
|
|
|
pid <- do
|
|
|
|
Entity s _ <- getBy404 $ UniqueSharer dev
|
|
|
|
Entity p _ <- getBy404 $ UniquePersonIdent s
|
|
|
|
return p
|
2016-06-06 17:29:54 +00:00
|
|
|
Entity cid _collab <- getBy404 $ UniqueProjectCollab jid pid
|
2016-06-01 08:52:14 +00:00
|
|
|
delete cid
|
|
|
|
setMessage "Collaborator removed."
|
|
|
|
redirect $ ProjectDevsR shr rp
|
|
|
|
|
|
|
|
postProjectDevR :: ShrIdent -> PrjIdent -> ShrIdent -> Handler Html
|
|
|
|
postProjectDevR shr rp dev = do
|
|
|
|
mmethod <- lookupPostParam "_method"
|
|
|
|
case mmethod of
|
|
|
|
Just "DELETE" -> deleteProjectDevR shr rp dev
|
|
|
|
_ -> notFound
|
2019-06-11 12:19:51 +00:00
|
|
|
|
|
|
|
getProjectTeamR :: ShrIdent -> PrjIdent -> Handler TypedContent
|
|
|
|
getProjectTeamR shr prj = do
|
|
|
|
memberShrs <- runDB $ do
|
|
|
|
sid <- getKeyBy404 $ UniqueSharer shr
|
|
|
|
_jid <- getKeyBy404 $ UniqueProject prj sid
|
|
|
|
id_ <-
|
|
|
|
requireEitherAlt
|
|
|
|
(getKeyBy $ UniquePersonIdent sid)
|
|
|
|
(getKeyBy $ UniqueGroup sid)
|
|
|
|
"Found sharer that is neither person nor group"
|
|
|
|
"Found sharer that is both person and group"
|
|
|
|
case id_ of
|
|
|
|
Left pid -> return [shr]
|
|
|
|
Right gid -> do
|
|
|
|
pids <-
|
|
|
|
map (groupMemberPerson . entityVal) <$>
|
|
|
|
selectList [GroupMemberGroup ==. gid] []
|
|
|
|
sids <-
|
|
|
|
map (personIdent . entityVal) <$>
|
|
|
|
selectList [PersonId <-. pids] []
|
|
|
|
map (sharerIdent . entityVal) <$>
|
|
|
|
selectList [SharerId <-. sids] []
|
|
|
|
|
|
|
|
let here = ProjectTeamR shr prj
|
|
|
|
|
|
|
|
encodeRouteLocal <- getEncodeRouteLocal
|
|
|
|
encodeRouteHome <- getEncodeRouteHome
|
|
|
|
let team = Collection
|
|
|
|
{ collectionId = encodeRouteLocal here
|
|
|
|
, collectionType = CollectionTypeUnordered
|
|
|
|
, collectionTotalItems = Just $ length memberShrs
|
|
|
|
, collectionCurrent = Nothing
|
|
|
|
, collectionFirst = Nothing
|
|
|
|
, collectionLast = Nothing
|
|
|
|
, collectionItems = map (encodeRouteHome . SharerR) memberShrs
|
|
|
|
}
|
|
|
|
provideHtmlAndAP team $ redirect (here, [("prettyjson", "true")])
|
|
|
|
|
|
|
|
getProjectFollowersR :: ShrIdent -> PrjIdent -> Handler TypedContent
|
|
|
|
getProjectFollowersR shr prj = getFollowersCollection here getFsid
|
|
|
|
where
|
|
|
|
here = ProjectFollowersR shr prj
|
|
|
|
getFsid = do
|
|
|
|
sid <- getKeyBy404 $ UniqueSharer shr
|
|
|
|
j <- getValBy404 $ UniqueProject prj sid
|
|
|
|
return $ projectFollowers j
|