Add GET routes and handlers for sharer-hosted patches
This commit is contained in:
parent
bb6785de75
commit
02c42029d2
21 changed files with 618 additions and 73 deletions
|
@ -443,6 +443,10 @@ TicketUnderProject
|
||||||
UniqueTicketUnderProjectProject project
|
UniqueTicketUnderProjectProject project
|
||||||
UniqueTicketUnderProjectAuthor author
|
UniqueTicketUnderProjectAuthor author
|
||||||
|
|
||||||
|
Patch
|
||||||
|
ticket TicketId
|
||||||
|
content Text
|
||||||
|
|
||||||
TicketDependency
|
TicketDependency
|
||||||
parent TicketId
|
parent TicketId
|
||||||
child TicketId
|
child TicketId
|
||||||
|
|
|
@ -192,4 +192,13 @@
|
||||||
/s/#ShrIdent/t/#TicketAuthorLocalKeyHashid/team SharerTicketTeamR GET
|
/s/#ShrIdent/t/#TicketAuthorLocalKeyHashid/team SharerTicketTeamR GET
|
||||||
/s/#ShrIdent/t/#TicketAuthorLocalKeyHashid/events SharerTicketEventsR GET
|
/s/#ShrIdent/t/#TicketAuthorLocalKeyHashid/events SharerTicketEventsR GET
|
||||||
|
|
||||||
|
/s/#ShrIdent/pt SharerPatchesR GET
|
||||||
|
|
||||||
|
/s/#ShrIdent/pt/#TicketAuthorLocalKeyHashid SharerPatchR GET
|
||||||
|
/s/#ShrIdent/pt/#TicketAuthorLocalKeyHashid/d SharerPatchDiscussionR GET
|
||||||
|
/s/#ShrIdent/pt/#TicketAuthorLocalKeyHashid/deps SharerPatchDepsR GET
|
||||||
|
/s/#ShrIdent/pt/#TicketAuthorLocalKeyHashid/rdeps SharerPatchReverseDepsR GET
|
||||||
|
/s/#ShrIdent/pt/#TicketAuthorLocalKeyHashid/followers SharerPatchFollowersR GET
|
||||||
|
/s/#ShrIdent/pt/#TicketAuthorLocalKeyHashid/events SharerPatchEventsR GET
|
||||||
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/w/+Texts WikiPageR GET
|
/s/#ShrIdent/p/#PrjIdent/w/+Texts WikiPageR GET
|
||||||
|
|
3
migrations/2020_05_17_patch.model
Normal file
3
migrations/2020_05_17_patch.model
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
Patch
|
||||||
|
ticket TicketId
|
||||||
|
content Text
|
66
src/Data/Patch/Local.hs
Normal file
66
src/Data/Patch/Local.hs
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
{- This file is part of Vervis.
|
||||||
|
-
|
||||||
|
- Written in 2018 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/>.
|
||||||
|
-}
|
||||||
|
|
||||||
|
-- | Representation of a commit in a repo for viewing.
|
||||||
|
--
|
||||||
|
-- Each version control system has its own specific details of how repository
|
||||||
|
-- changes are represented and encoded and stored internally. This module is
|
||||||
|
-- merely a model for displaying a commit to a human viewer.
|
||||||
|
module Data.Patch.Local
|
||||||
|
( Hunk (..)
|
||||||
|
, Edit (..)
|
||||||
|
, Author (..)
|
||||||
|
, Patch (..)
|
||||||
|
)
|
||||||
|
where
|
||||||
|
|
||||||
|
import Data.Int (Int64)
|
||||||
|
import Data.List.NonEmpty (NonEmpty)
|
||||||
|
import Data.Text (Text)
|
||||||
|
import Data.Time.Clock (UTCTime)
|
||||||
|
import Data.Word (Word32)
|
||||||
|
import Data.Vector (Vector)
|
||||||
|
import Text.Email.Validate (EmailAddress)
|
||||||
|
|
||||||
|
data Hunk = Hunk
|
||||||
|
{ hunkAddFirst :: [Text]
|
||||||
|
, hunkRemoveAdd :: [(NonEmpty Text, NonEmpty Text)]
|
||||||
|
, hunkRemoveLast :: [Text]
|
||||||
|
}
|
||||||
|
|
||||||
|
data Edit
|
||||||
|
= AddTextFile FilePath Word32 [Text]
|
||||||
|
| AddBinaryFile FilePath Word32 Int64
|
||||||
|
| RemoveTextFile FilePath Word32 [Text]
|
||||||
|
| RemoveBinaryFile FilePath Word32 Int64
|
||||||
|
| MoveFile FilePath Word32 FilePath Word32
|
||||||
|
| ChmodFile FilePath Word32 Word32
|
||||||
|
| EditTextFile FilePath (Vector Text) (NonEmpty (Bool, Int, Hunk)) Word32 Word32
|
||||||
|
| EditBinaryFile FilePath Int64 Word32 Int64 Word32
|
||||||
|
| TextToBinary FilePath [Text] Word32 Int64 Word32
|
||||||
|
| BinaryToText FilePath Int64 Word32 [Text] Word32
|
||||||
|
|
||||||
|
data Author = Author
|
||||||
|
{ authorName :: Text
|
||||||
|
, authorEmail :: EmailAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
data Patch = Patch
|
||||||
|
{ patchWritten :: (Author, UTCTime)
|
||||||
|
, patchCommitted :: Maybe (Author, UTCTime)
|
||||||
|
, patchTitle :: Text
|
||||||
|
, patchDescription :: Text
|
||||||
|
, patchDiff :: [Edit]
|
||||||
|
}
|
|
@ -655,7 +655,7 @@ createTicketC (Entity pidUser personUser) sharerUser summary audience ticket muT
|
||||||
{ ticketId = encodeRouteLocal $ SharerTicketR shrUser talkhid
|
{ ticketId = encodeRouteLocal $ SharerTicketR shrUser talkhid
|
||||||
, ticketReplies = encodeRouteLocal $ SharerTicketDiscussionR shrUser talkhid
|
, ticketReplies = encodeRouteLocal $ SharerTicketDiscussionR shrUser talkhid
|
||||||
, ticketParticipants = encodeRouteLocal $ SharerTicketFollowersR shrUser talkhid
|
, ticketParticipants = encodeRouteLocal $ SharerTicketFollowersR shrUser talkhid
|
||||||
, ticketTeam = encodeRouteLocal $ SharerTicketTeamR shrUser talkhid
|
, ticketTeam = Just $ encodeRouteLocal $ SharerTicketTeamR shrUser talkhid
|
||||||
, ticketEvents = encodeRouteLocal $ SharerTicketEventsR shrUser talkhid
|
, ticketEvents = encodeRouteLocal $ SharerTicketEventsR shrUser talkhid
|
||||||
, ticketDeps = encodeRouteLocal $ SharerTicketDepsR shrUser talkhid
|
, ticketDeps = encodeRouteLocal $ SharerTicketDepsR shrUser talkhid
|
||||||
, ticketReverseDeps = encodeRouteLocal $ SharerTicketReverseDepsR shrUser talkhid
|
, ticketReverseDeps = encodeRouteLocal $ SharerTicketReverseDepsR shrUser talkhid
|
||||||
|
|
|
@ -109,15 +109,17 @@ import qualified Web.ActivityPub as AP
|
||||||
import Control.Monad.Trans.Except.Local
|
import Control.Monad.Trans.Except.Local
|
||||||
import Data.Either.Local
|
import Data.Either.Local
|
||||||
import Data.List.NonEmpty.Local
|
import Data.List.NonEmpty.Local
|
||||||
|
import Data.Patch.Local hiding (Patch)
|
||||||
import Data.Tuple.Local
|
import Data.Tuple.Local
|
||||||
import Database.Persist.Local
|
import Database.Persist.Local
|
||||||
|
|
||||||
|
import qualified Data.Patch.Local as P
|
||||||
|
|
||||||
import Vervis.ActivityPub.Recipient
|
import Vervis.ActivityPub.Recipient
|
||||||
import Vervis.FedURI
|
import Vervis.FedURI
|
||||||
import Vervis.Foundation
|
import Vervis.Foundation
|
||||||
import Vervis.Model
|
import Vervis.Model
|
||||||
import Vervis.Model.Ident
|
import Vervis.Model.Ident
|
||||||
import Vervis.Patch
|
|
||||||
import Vervis.RemoteActorStore
|
import Vervis.RemoteActorStore
|
||||||
import Vervis.Settings
|
import Vervis.Settings
|
||||||
import Vervis.Time
|
import Vervis.Time
|
||||||
|
@ -718,7 +720,7 @@ serveCommit
|
||||||
:: ShrIdent
|
:: ShrIdent
|
||||||
-> RpIdent
|
-> RpIdent
|
||||||
-> Text
|
-> Text
|
||||||
-> Patch
|
-> P.Patch
|
||||||
-> [Text]
|
-> [Text]
|
||||||
-> Handler TypedContent
|
-> Handler TypedContent
|
||||||
serveCommit shr rp ref patch parents = do
|
serveCommit shr rp ref patch parents = do
|
||||||
|
|
|
@ -108,6 +108,7 @@ import Vervis.Handler.Group
|
||||||
import Vervis.Handler.Home
|
import Vervis.Handler.Home
|
||||||
import Vervis.Handler.Inbox
|
import Vervis.Handler.Inbox
|
||||||
import Vervis.Handler.Key
|
import Vervis.Handler.Key
|
||||||
|
import Vervis.Handler.Patch
|
||||||
import Vervis.Handler.Person
|
import Vervis.Handler.Person
|
||||||
import Vervis.Handler.Project
|
import Vervis.Handler.Project
|
||||||
import Vervis.Handler.Repo
|
import Vervis.Handler.Repo
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{- This file is part of Vervis.
|
{- This file is part of Vervis.
|
||||||
-
|
-
|
||||||
- Written in 2016, 2018, 2019 by fr33domlover <fr33domlover@riseup.net>.
|
- Written in 2016, 2018, 2019, 2020 by fr33domlover <fr33domlover@riseup.net>.
|
||||||
-
|
-
|
||||||
- ♡ Copying is an act of love. Please copy, reuse and share.
|
- ♡ Copying is an act of love. Please copy, reuse and share.
|
||||||
-
|
-
|
||||||
|
@ -73,15 +73,17 @@ import Data.Either.Local (maybeRight)
|
||||||
import Data.EventTime.Local
|
import Data.EventTime.Local
|
||||||
import Data.List.Local
|
import Data.List.Local
|
||||||
import Data.List.NonEmpty.Local
|
import Data.List.NonEmpty.Local
|
||||||
|
import Data.Patch.Local hiding (Patch)
|
||||||
import Data.Text.UTF8.Local (decodeStrict)
|
import Data.Text.UTF8.Local (decodeStrict)
|
||||||
import Data.Time.Clock.Local ()
|
import Data.Time.Clock.Local ()
|
||||||
|
|
||||||
|
import qualified Data.Patch.Local as DP
|
||||||
|
|
||||||
import Vervis.Changes
|
import Vervis.Changes
|
||||||
import Vervis.Foundation
|
import Vervis.Foundation
|
||||||
import Vervis.Model
|
import Vervis.Model
|
||||||
import Vervis.Model.Ident
|
import Vervis.Model.Ident
|
||||||
import Vervis.Model.Repo
|
import Vervis.Model.Repo
|
||||||
import Vervis.Patch
|
|
||||||
import Vervis.Path
|
import Vervis.Path
|
||||||
import Vervis.Readme
|
import Vervis.Readme
|
||||||
import Vervis.Settings
|
import Vervis.Settings
|
||||||
|
@ -309,7 +311,7 @@ joinHunks =
|
||||||
-- the expected format. If not, an exception is thrown.
|
-- the expected format. If not, an exception is thrown.
|
||||||
-- * The hash may or may not be found in the repo. If there's no patch in the
|
-- * The hash may or may not be found in the repo. If there's no patch in the
|
||||||
-- repo with the given hash, 'Nothing' is returned.
|
-- repo with the given hash, 'Nothing' is returned.
|
||||||
readPatch :: FilePath -> Text -> IO (Maybe Patch)
|
readPatch :: FilePath -> Text -> IO (Maybe DP.Patch)
|
||||||
readPatch path hash = handle $ runExceptT $ do
|
readPatch path hash = handle $ runExceptT $ do
|
||||||
let pih = PatchInfoHash $ fst $ B16.decode $ encodeUtf8 hash
|
let pih = PatchInfoHash $ fst $ B16.decode $ encodeUtf8 hash
|
||||||
li <- ExceptT $ readLatestInventory path latestInventoryAllP
|
li <- ExceptT $ readLatestInventory path latestInventoryAllP
|
||||||
|
@ -319,7 +321,7 @@ readPatch path hash = handle $ runExceptT $ do
|
||||||
ExceptT $ readCompressedPatch path pch (P.patch <* A.endOfInput)
|
ExceptT $ readCompressedPatch path pch (P.patch <* A.endOfInput)
|
||||||
(an, ae) <-
|
(an, ae) <-
|
||||||
ExceptT . pure $ A.parseOnly (author <* A.endOfInput) $ piAuthor pi
|
ExceptT . pure $ A.parseOnly (author <* A.endOfInput) $ piAuthor pi
|
||||||
return Patch
|
return DP.Patch
|
||||||
{ patchWritten =
|
{ patchWritten =
|
||||||
( Author
|
( Author
|
||||||
{ authorName = an
|
{ authorName = an
|
||||||
|
|
|
@ -645,7 +645,7 @@ projectCreateTicketF now shrRecip prjRecip author body ticket muTarget = do
|
||||||
remoteRecipsC = catMaybes
|
remoteRecipsC = catMaybes
|
||||||
[ remoteActorFollowers ra
|
[ remoteActorFollowers ra
|
||||||
, Just $ AP.ticketParticipants tlocal
|
, Just $ AP.ticketParticipants tlocal
|
||||||
, Just $ AP.ticketTeam tlocal
|
, AP.ticketTeam tlocal
|
||||||
]
|
]
|
||||||
localRecips =
|
localRecips =
|
||||||
map encodeRouteHome $
|
map encodeRouteHome $
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{- This file is part of Vervis.
|
{- This file is part of Vervis.
|
||||||
-
|
-
|
||||||
- Written in 2016, 2018, 2019 by fr33domlover <fr33domlover@riseup.net>.
|
- Written in 2016, 2018, 2019, 2020 by fr33domlover <fr33domlover@riseup.net>.
|
||||||
-
|
-
|
||||||
- ♡ Copying is an act of love. Please copy, reuse and share.
|
- ♡ Copying is an act of love. Please copy, reuse and share.
|
||||||
-
|
-
|
||||||
|
@ -76,13 +76,15 @@ import Data.DList.Local
|
||||||
import Data.EventTime.Local
|
import Data.EventTime.Local
|
||||||
import Data.Git.Local
|
import Data.Git.Local
|
||||||
import Data.List.Local
|
import Data.List.Local
|
||||||
|
import Data.Patch.Local hiding (Patch)
|
||||||
|
|
||||||
|
import qualified Data.Patch.Local as P
|
||||||
|
|
||||||
import Vervis.Changes
|
import Vervis.Changes
|
||||||
import Vervis.Foundation
|
import Vervis.Foundation
|
||||||
import Vervis.Model
|
import Vervis.Model
|
||||||
import Vervis.Model.Ident
|
import Vervis.Model.Ident
|
||||||
import Vervis.Model.Repo
|
import Vervis.Model.Repo
|
||||||
import Vervis.Patch
|
|
||||||
import Vervis.Path
|
import Vervis.Path
|
||||||
import Vervis.Readme
|
import Vervis.Readme
|
||||||
import Vervis.Settings
|
import Vervis.Settings
|
||||||
|
@ -204,8 +206,8 @@ listRefs :: FilePath -> IO (Set Text, Set Text)
|
||||||
listRefs path = G.withRepo (fromString path) $ \ git ->
|
listRefs path = G.withRepo (fromString path) $ \ git ->
|
||||||
(,) <$> listBranches git <*> listTags git
|
(,) <$> listBranches git <*> listTags git
|
||||||
|
|
||||||
patch :: [Edit] -> Commit SHA1 -> Patch
|
patch :: [Edit] -> Commit SHA1 -> P.Patch
|
||||||
patch edits c = Patch
|
patch edits c = P.Patch
|
||||||
{ patchWritten = makeAuthor $ commitAuthor c
|
{ patchWritten = makeAuthor $ commitAuthor c
|
||||||
, patchCommitted =
|
, patchCommitted =
|
||||||
if commitAuthor c == commitCommitter c
|
if commitAuthor c == commitCommitter c
|
||||||
|
@ -299,7 +301,7 @@ accumEdits (OldAndNew old new) es =
|
||||||
(BinaryContent from, BinaryContent to) -> EditBinaryFile (ep2fp $ bsFilename new) (BL.length from) (unModePerm $ bsMode old) (BL.length to) (unModePerm $ bsMode new) : es
|
(BinaryContent from, BinaryContent to) -> EditBinaryFile (ep2fp $ bsFilename new) (BL.length from) (unModePerm $ bsMode old) (BL.length to) (unModePerm $ bsMode new) : es
|
||||||
else error "getDiffWith gave OldAndNew with different file paths"
|
else error "getDiffWith gave OldAndNew with different file paths"
|
||||||
|
|
||||||
readPatch :: FilePath -> Text -> IO (Patch, [Text])
|
readPatch :: FilePath -> Text -> IO (P.Patch, [Text])
|
||||||
readPatch path hash = G.withRepo (fromString path) $ \ git -> do
|
readPatch path hash = G.withRepo (fromString path) $ \ git -> do
|
||||||
let ref = fromHex $ encodeUtf8 hash
|
let ref = fromHex $ encodeUtf8 hash
|
||||||
c <- G.getCommit git ref
|
c <- G.getCommit git ref
|
||||||
|
|
326
src/Vervis/Handler/Patch.hs
Normal file
326
src/Vervis/Handler/Patch.hs
Normal file
|
@ -0,0 +1,326 @@
|
||||||
|
{- This file is part of Vervis.
|
||||||
|
-
|
||||||
|
- Written in 2020 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.Patch
|
||||||
|
( getSharerPatchesR
|
||||||
|
, getSharerPatchR
|
||||||
|
, getSharerPatchDiscussionR
|
||||||
|
, getSharerPatchDepsR
|
||||||
|
, getSharerPatchReverseDepsR
|
||||||
|
, getSharerPatchFollowersR
|
||||||
|
, getSharerPatchEventsR
|
||||||
|
)
|
||||||
|
where
|
||||||
|
|
||||||
|
import Data.Bitraversable
|
||||||
|
import Data.Text (Text)
|
||||||
|
import Data.Traversable
|
||||||
|
import Database.Persist
|
||||||
|
import Yesod.Core
|
||||||
|
import Yesod.Persist.Core
|
||||||
|
|
||||||
|
import qualified Database.Esqueleto as E
|
||||||
|
|
||||||
|
import Network.FedURI
|
||||||
|
import Web.ActivityPub hiding (Ticket (..))
|
||||||
|
import Yesod.ActivityPub
|
||||||
|
import Yesod.FedURI
|
||||||
|
import Yesod.Hashids
|
||||||
|
|
||||||
|
import qualified Web.ActivityPub as AP
|
||||||
|
|
||||||
|
import Data.Paginate.Local
|
||||||
|
import Yesod.Persist.Local
|
||||||
|
|
||||||
|
import Vervis.API
|
||||||
|
import Vervis.FedURI
|
||||||
|
import Vervis.Foundation
|
||||||
|
import Vervis.Model
|
||||||
|
import Vervis.Model.Ident
|
||||||
|
import Vervis.Model.Ticket
|
||||||
|
import Vervis.Paginate
|
||||||
|
import Vervis.Patch
|
||||||
|
|
||||||
|
getSharerPatchesR :: ShrIdent -> Handler TypedContent
|
||||||
|
getSharerPatchesR shr = do
|
||||||
|
(total, pages, mpage) <- runDB $ do
|
||||||
|
sid <- getKeyBy404 $ UniqueSharer shr
|
||||||
|
pid <- getKeyBy404 $ UniquePersonIdent sid
|
||||||
|
getPageAndNavCount (countPatches pid) (selectPatches pid)
|
||||||
|
encodeRouteHome <- getEncodeRouteHome
|
||||||
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
|
encodeRoutePageLocal <- getEncodeRoutePageLocal
|
||||||
|
let pageUrl = encodeRoutePageLocal here
|
||||||
|
encodeTicketKey <- getEncodeKeyHashid
|
||||||
|
let patchUrl = SharerPatchR shr . encodeTicketKey
|
||||||
|
|
||||||
|
case mpage of
|
||||||
|
Nothing -> provide $ Collection
|
||||||
|
{ collectionId = encodeRouteLocal here
|
||||||
|
, collectionType = CollectionTypeOrdered
|
||||||
|
, collectionTotalItems = Just total
|
||||||
|
, collectionCurrent = Nothing
|
||||||
|
, collectionFirst = Just $ pageUrl 1
|
||||||
|
, collectionLast = Just $ pageUrl pages
|
||||||
|
, collectionItems = [] :: [Text]
|
||||||
|
}
|
||||||
|
Just (patches, navModel) ->
|
||||||
|
let current = nmCurrent navModel
|
||||||
|
in provide $ CollectionPage
|
||||||
|
{ collectionPageId = pageUrl current
|
||||||
|
, collectionPageType = CollectionPageTypeOrdered
|
||||||
|
, collectionPageTotalItems = Nothing
|
||||||
|
, collectionPageCurrent = Just $ pageUrl current
|
||||||
|
, collectionPageFirst = Just $ pageUrl 1
|
||||||
|
, collectionPageLast = Just $ pageUrl pages
|
||||||
|
, collectionPagePartOf = encodeRouteLocal here
|
||||||
|
, collectionPagePrev =
|
||||||
|
if current > 1
|
||||||
|
then Just $ pageUrl $ current - 1
|
||||||
|
else Nothing
|
||||||
|
, collectionPageNext =
|
||||||
|
if current < pages
|
||||||
|
then Just $ pageUrl $ current + 1
|
||||||
|
else Nothing
|
||||||
|
, collectionPageStartIndex = Nothing
|
||||||
|
, collectionPageItems =
|
||||||
|
map (encodeRouteHome . patchUrl . E.unValue) patches
|
||||||
|
}
|
||||||
|
where
|
||||||
|
here = SharerPatchesR shr
|
||||||
|
provide :: ActivityPub a => a URIMode -> Handler TypedContent
|
||||||
|
provide a = provideHtmlAndAP a $ redirectToPrettyJSON here
|
||||||
|
countPatches pid = fmap toOne $
|
||||||
|
E.select $ E.from $ \ (tal `E.InnerJoin` lt `E.LeftOuterJoin` tup) -> do
|
||||||
|
E.on $ E.just (tal E.^. TicketAuthorLocalId) E.==. tup E.?. TicketUnderProjectAuthor
|
||||||
|
E.on $ tal E.^. TicketAuthorLocalTicket E.==. lt E.^. LocalTicketId
|
||||||
|
E.where_ $
|
||||||
|
tal E.^. TicketAuthorLocalAuthor E.==. E.val pid E.&&.
|
||||||
|
E.isNothing (tup E.?. TicketUnderProjectId) E.&&.
|
||||||
|
E.exists
|
||||||
|
(E.from $ \ pt ->
|
||||||
|
E.where_ $ lt E.^. LocalTicketTicket E.==. pt E.^. PatchTicket
|
||||||
|
)
|
||||||
|
return $ E.count $ tal E.^. TicketAuthorLocalId
|
||||||
|
where
|
||||||
|
toOne [x] = E.unValue x
|
||||||
|
toOne [] = error "toOne = 0"
|
||||||
|
toOne _ = error "toOne > 1"
|
||||||
|
selectPatches pid off lim =
|
||||||
|
E.select $ E.from $ \ (tal `E.InnerJoin` lt `E.LeftOuterJoin` tup) -> do
|
||||||
|
E.on $ E.just (tal E.^. TicketAuthorLocalId) E.==. tup E.?. TicketUnderProjectAuthor
|
||||||
|
E.on $ tal E.^. TicketAuthorLocalTicket E.==. lt E.^. LocalTicketId
|
||||||
|
E.where_ $
|
||||||
|
tal E.^. TicketAuthorLocalAuthor E.==. E.val pid E.&&.
|
||||||
|
E.isNothing (tup E.?. TicketUnderProjectId) E.&&.
|
||||||
|
E.exists
|
||||||
|
(E.from $ \ pt ->
|
||||||
|
E.where_ $ lt E.^. LocalTicketTicket E.==. pt E.^. PatchTicket
|
||||||
|
)
|
||||||
|
E.orderBy [E.desc $ tal E.^. TicketAuthorLocalId]
|
||||||
|
E.offset $ fromIntegral off
|
||||||
|
E.limit $ fromIntegral lim
|
||||||
|
return $ tal E.^. TicketAuthorLocalId
|
||||||
|
|
||||||
|
getSharerPatchR
|
||||||
|
:: ShrIdent -> KeyHashid TicketAuthorLocal -> Handler TypedContent
|
||||||
|
getSharerPatchR shr talkhid = do
|
||||||
|
(ticket, repo, massignee) <- runDB $ do
|
||||||
|
(_, _, Entity _ t, tp) <- getSharerPatch404 shr talkhid
|
||||||
|
(,,) t
|
||||||
|
<$> bitraverse
|
||||||
|
(\ (_, Entity _ trl) -> do
|
||||||
|
r <- getJust $ ticketRepoLocalRepo trl
|
||||||
|
s <- getJust $ repoSharer r
|
||||||
|
return (s, r)
|
||||||
|
)
|
||||||
|
(\ (Entity _ tpr, _) -> do
|
||||||
|
roid <-
|
||||||
|
case ticketProjectRemoteProject tpr of
|
||||||
|
Nothing ->
|
||||||
|
remoteActorIdent <$>
|
||||||
|
getJust (ticketProjectRemoteTracker tpr)
|
||||||
|
Just roid -> return roid
|
||||||
|
ro <- getJust roid
|
||||||
|
i <- getJust $ remoteObjectInstance ro
|
||||||
|
return (i, ro)
|
||||||
|
)
|
||||||
|
tp
|
||||||
|
<*> (for (ticketAssignee t) $ \ pidAssignee -> do
|
||||||
|
p <- getJust pidAssignee
|
||||||
|
getJust $ personIdent p
|
||||||
|
)
|
||||||
|
hLocal <- getsYesod siteInstanceHost
|
||||||
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
|
encodeRouteHome <- getEncodeRouteHome
|
||||||
|
let patchAP = AP.Ticket
|
||||||
|
{ AP.ticketLocal = Just
|
||||||
|
( hLocal
|
||||||
|
, AP.TicketLocal
|
||||||
|
{ AP.ticketId =
|
||||||
|
encodeRouteLocal $ SharerPatchR shr talkhid
|
||||||
|
, AP.ticketReplies =
|
||||||
|
encodeRouteLocal $ SharerPatchDiscussionR shr talkhid
|
||||||
|
, AP.ticketParticipants =
|
||||||
|
encodeRouteLocal $ SharerPatchFollowersR shr talkhid
|
||||||
|
, AP.ticketTeam = Nothing
|
||||||
|
, AP.ticketEvents =
|
||||||
|
encodeRouteLocal $ SharerPatchEventsR shr talkhid
|
||||||
|
, AP.ticketDeps =
|
||||||
|
encodeRouteLocal $ SharerPatchDepsR shr talkhid
|
||||||
|
, AP.ticketReverseDeps =
|
||||||
|
encodeRouteLocal $ SharerPatchReverseDepsR shr talkhid
|
||||||
|
}
|
||||||
|
)
|
||||||
|
, AP.ticketAttributedTo = encodeRouteLocal $ SharerR shr
|
||||||
|
, AP.ticketPublished = Just $ ticketCreated ticket
|
||||||
|
, AP.ticketUpdated = Nothing
|
||||||
|
, AP.ticketContext =
|
||||||
|
Just $
|
||||||
|
case repo of
|
||||||
|
Left (s, r) ->
|
||||||
|
encodeRouteHome $
|
||||||
|
RepoR (sharerIdent s) (repoIdent r)
|
||||||
|
Right (i, ro) ->
|
||||||
|
ObjURI (instanceHost i) (remoteObjectIdent ro)
|
||||||
|
, AP.ticketSummary = TextHtml $ ticketTitle ticket
|
||||||
|
, AP.ticketContent = TextHtml $ ticketDescription ticket
|
||||||
|
, AP.ticketSource = TextPandocMarkdown $ ticketSource ticket
|
||||||
|
, AP.ticketAssignedTo =
|
||||||
|
encodeRouteHome . SharerR . sharerIdent <$> massignee
|
||||||
|
, AP.ticketIsResolved = ticketStatus ticket == TSClosed
|
||||||
|
}
|
||||||
|
provideHtmlAndAP patchAP $ redirectToPrettyJSON here
|
||||||
|
where
|
||||||
|
here = SharerPatchR shr talkhid
|
||||||
|
|
||||||
|
getSharerPatchDiscussionR
|
||||||
|
:: ShrIdent -> KeyHashid TicketAuthorLocal -> Handler TypedContent
|
||||||
|
getSharerPatchDiscussionR shr talkhid = do
|
||||||
|
(locals, remotes) <- runDB $ do
|
||||||
|
(_, Entity _ lt, _, _) <- getSharerPatch404 shr talkhid
|
||||||
|
let did = localTicketDiscuss lt
|
||||||
|
(,) <$> selectLocals did <*> selectRemotes did
|
||||||
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
|
encodeRouteHome <- getEncodeRouteHome
|
||||||
|
encodeHid <- getEncodeKeyHashid
|
||||||
|
let localUri' = localUri encodeRouteHome encodeHid
|
||||||
|
replies = Collection
|
||||||
|
{ collectionId = encodeRouteLocal here
|
||||||
|
, collectionType = CollectionTypeUnordered
|
||||||
|
, collectionTotalItems = Just $ length locals + length remotes
|
||||||
|
, collectionCurrent = Nothing
|
||||||
|
, collectionFirst = Nothing
|
||||||
|
, collectionLast = Nothing
|
||||||
|
, collectionItems =
|
||||||
|
map localUri' locals ++ map remoteUri remotes
|
||||||
|
}
|
||||||
|
provideHtmlAndAP replies $ redirectToPrettyJSON here
|
||||||
|
where
|
||||||
|
here = SharerPatchDiscussionR shr talkhid
|
||||||
|
selectLocals did =
|
||||||
|
E.select $ E.from $
|
||||||
|
\ (m `E.InnerJoin` lm `E.InnerJoin` p `E.InnerJoin` s) -> do
|
||||||
|
E.on $ p E.^. PersonIdent E.==. s E.^. SharerId
|
||||||
|
E.on $ lm E.^. LocalMessageAuthor E.==. p E.^. PersonId
|
||||||
|
E.on $ m E.^. MessageId E.==. lm E.^. LocalMessageRest
|
||||||
|
E.where_ $
|
||||||
|
m E.^. MessageRoot E.==. E.val did E.&&.
|
||||||
|
E.isNothing (m E.^. MessageParent) E.&&.
|
||||||
|
E.isNothing (lm E.^. LocalMessageUnlinkedParent)
|
||||||
|
return (s E.^. SharerIdent, lm E.^. LocalMessageId)
|
||||||
|
selectRemotes did =
|
||||||
|
E.select $ E.from $
|
||||||
|
\ (m `E.InnerJoin` rm `E.InnerJoin` ro `E.InnerJoin` i) -> do
|
||||||
|
E.on $ ro E.^. RemoteObjectInstance E.==. i E.^. InstanceId
|
||||||
|
E.on $ rm E.^. RemoteMessageIdent E.==. ro E.^. RemoteObjectId
|
||||||
|
E.on $ m E.^. MessageId E.==. rm E.^. RemoteMessageRest
|
||||||
|
E.where_ $
|
||||||
|
m E.^. MessageRoot E.==. E.val did E.&&.
|
||||||
|
E.isNothing (m E.^. MessageParent) E.&&.
|
||||||
|
E.isNothing (rm E.^. RemoteMessageLostParent)
|
||||||
|
return (i E.^. InstanceHost, ro E.^. RemoteObjectIdent)
|
||||||
|
localUri encR encH (E.Value shrAuthor, E.Value lmid) =
|
||||||
|
encR $ MessageR shrAuthor (encH lmid)
|
||||||
|
remoteUri (E.Value h, E.Value lu) = ObjURI h lu
|
||||||
|
|
||||||
|
getSharerPatchDeps
|
||||||
|
:: Bool -> ShrIdent -> KeyHashid TicketAuthorLocal -> Handler TypedContent
|
||||||
|
getSharerPatchDeps forward shr talkhid = do
|
||||||
|
tdids <- runDB $ do
|
||||||
|
(_, _, Entity tid _, _) <- getSharerPatch404 shr talkhid
|
||||||
|
let (from, to) =
|
||||||
|
if forward
|
||||||
|
then (TicketDependencyParent, TicketDependencyChild)
|
||||||
|
else (TicketDependencyChild, TicketDependencyParent)
|
||||||
|
E.select $ E.from $ \ (td `E.InnerJoin` t) -> do
|
||||||
|
E.on $ td E.^. to E.==. t E.^. TicketId
|
||||||
|
E.where_ $ td E.^. from E.==. E.val tid
|
||||||
|
return $ td E.^. TicketDependencyId
|
||||||
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
|
encodeRouteHome <- getEncodeRouteHome
|
||||||
|
encodeHid <- getEncodeKeyHashid
|
||||||
|
let deps = Collection
|
||||||
|
{ collectionId = encodeRouteLocal here
|
||||||
|
, collectionType = CollectionTypeUnordered
|
||||||
|
, collectionTotalItems = Just $ length tdids
|
||||||
|
, collectionCurrent = Nothing
|
||||||
|
, collectionFirst = Nothing
|
||||||
|
, collectionLast = Nothing
|
||||||
|
, collectionItems =
|
||||||
|
map (encodeRouteHome . TicketDepR . encodeHid . E.unValue)
|
||||||
|
tdids
|
||||||
|
}
|
||||||
|
provideHtmlAndAP deps $ redirectToPrettyJSON here
|
||||||
|
where
|
||||||
|
here =
|
||||||
|
let route =
|
||||||
|
if forward then SharerPatchDepsR else SharerTicketReverseDepsR
|
||||||
|
in route shr talkhid
|
||||||
|
|
||||||
|
getSharerPatchDepsR
|
||||||
|
:: ShrIdent -> KeyHashid TicketAuthorLocal -> Handler TypedContent
|
||||||
|
getSharerPatchDepsR = getSharerPatchDeps True
|
||||||
|
|
||||||
|
getSharerPatchReverseDepsR
|
||||||
|
:: ShrIdent -> KeyHashid TicketAuthorLocal -> Handler TypedContent
|
||||||
|
getSharerPatchReverseDepsR = getSharerPatchDeps False
|
||||||
|
|
||||||
|
getSharerPatchFollowersR
|
||||||
|
:: ShrIdent -> KeyHashid TicketAuthorLocal -> Handler TypedContent
|
||||||
|
getSharerPatchFollowersR shr talkhid = getFollowersCollection here getFsid
|
||||||
|
where
|
||||||
|
here = SharerPatchFollowersR shr talkhid
|
||||||
|
getFsid = do
|
||||||
|
(_, Entity _ lt, _, _) <- getSharerPatch404 shr talkhid
|
||||||
|
return $ localTicketFollowers lt
|
||||||
|
|
||||||
|
getSharerPatchEventsR
|
||||||
|
:: ShrIdent -> KeyHashid TicketAuthorLocal -> Handler TypedContent
|
||||||
|
getSharerPatchEventsR shr talkhid = do
|
||||||
|
_ <- runDB $ getSharerPatch404 shr talkhid
|
||||||
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
|
let team = Collection
|
||||||
|
{ collectionId = encodeRouteLocal here
|
||||||
|
, collectionType = CollectionTypeOrdered
|
||||||
|
, collectionTotalItems = Just 0
|
||||||
|
, collectionCurrent = Nothing
|
||||||
|
, collectionFirst = Nothing
|
||||||
|
, collectionLast = Nothing
|
||||||
|
, collectionItems = [] :: [Text]
|
||||||
|
}
|
||||||
|
provideHtmlAndAP team $ redirectToPrettyJSON here
|
||||||
|
where
|
||||||
|
here = SharerPatchEventsR shr talkhid
|
|
@ -1,6 +1,6 @@
|
||||||
{- This file is part of Vervis.
|
{- This file is part of Vervis.
|
||||||
-
|
-
|
||||||
- Written in 2016, 2018, 2019 by fr33domlover <fr33domlover@riseup.net>.
|
- Written in 2016, 2018, 2019, 2020 by fr33domlover <fr33domlover@riseup.net>.
|
||||||
-
|
-
|
||||||
- ♡ Copying is an act of love. Please copy, reuse and share.
|
- ♡ Copying is an act of love. Please copy, reuse and share.
|
||||||
-
|
-
|
||||||
|
@ -55,6 +55,7 @@ import Yesod.RenderSource
|
||||||
|
|
||||||
import Data.ByteString.Char8.Local (takeLine)
|
import Data.ByteString.Char8.Local (takeLine)
|
||||||
import Data.Paginate.Local
|
import Data.Paginate.Local
|
||||||
|
import Data.Patch.Local
|
||||||
import Text.FilePath.Local (breakExt)
|
import Text.FilePath.Local (breakExt)
|
||||||
|
|
||||||
import Vervis.ActivityPub
|
import Vervis.ActivityPub
|
||||||
|
@ -67,7 +68,6 @@ import Vervis.Model
|
||||||
import Vervis.Model.Ident
|
import Vervis.Model.Ident
|
||||||
import Vervis.Model.Repo
|
import Vervis.Model.Repo
|
||||||
import Vervis.Paginate
|
import Vervis.Paginate
|
||||||
import Vervis.Patch
|
|
||||||
import Vervis.Readme
|
import Vervis.Readme
|
||||||
import Vervis.Settings
|
import Vervis.Settings
|
||||||
import Vervis.SourceTree
|
import Vervis.SourceTree
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{- This file is part of Vervis.
|
{- This file is part of Vervis.
|
||||||
-
|
-
|
||||||
- Written in 2016, 2018, 2019 by fr33domlover <fr33domlover@riseup.net>.
|
- Written in 2016, 2018, 2019, 2020 by fr33domlover <fr33domlover@riseup.net>.
|
||||||
-
|
-
|
||||||
- ♡ Copying is an act of love. Please copy, reuse and share.
|
- ♡ Copying is an act of love. Please copy, reuse and share.
|
||||||
-
|
-
|
||||||
|
@ -69,6 +69,7 @@ import qualified Web.ActivityPub as AP
|
||||||
import Data.ByteString.Char8.Local (takeLine)
|
import Data.ByteString.Char8.Local (takeLine)
|
||||||
import Data.Git.Local
|
import Data.Git.Local
|
||||||
import Data.Paginate.Local
|
import Data.Paginate.Local
|
||||||
|
import Data.Patch.Local
|
||||||
import Text.FilePath.Local (breakExt)
|
import Text.FilePath.Local (breakExt)
|
||||||
|
|
||||||
import Vervis.ActivityPub
|
import Vervis.ActivityPub
|
||||||
|
@ -81,7 +82,6 @@ import Vervis.Model
|
||||||
import Vervis.Model.Ident
|
import Vervis.Model.Ident
|
||||||
import Vervis.Model.Repo
|
import Vervis.Model.Repo
|
||||||
import Vervis.Paginate
|
import Vervis.Paginate
|
||||||
import Vervis.Patch
|
|
||||||
import Vervis.Readme
|
import Vervis.Readme
|
||||||
import Vervis.Settings
|
import Vervis.Settings
|
||||||
import Vervis.SourceTree
|
import Vervis.SourceTree
|
||||||
|
|
|
@ -389,7 +389,7 @@ getProjectTicketR shar proj ltkhid = do
|
||||||
, AP.ticketParticipants =
|
, AP.ticketParticipants =
|
||||||
encodeRouteLocal $ ProjectTicketParticipantsR shar proj ltkhid
|
encodeRouteLocal $ ProjectTicketParticipantsR shar proj ltkhid
|
||||||
, AP.ticketTeam =
|
, AP.ticketTeam =
|
||||||
encodeRouteLocal $ ProjectTicketTeamR shar proj ltkhid
|
Just $ encodeRouteLocal $ ProjectTicketTeamR shar proj ltkhid
|
||||||
, AP.ticketEvents =
|
, AP.ticketEvents =
|
||||||
encodeRouteLocal $ ProjectTicketEventsR shar proj ltkhid
|
encodeRouteLocal $ ProjectTicketEventsR shar proj ltkhid
|
||||||
, AP.ticketDeps =
|
, AP.ticketDeps =
|
||||||
|
@ -1178,22 +1178,28 @@ getSharerTicketsR shr = do
|
||||||
provide :: ActivityPub a => a URIMode -> Handler TypedContent
|
provide :: ActivityPub a => a URIMode -> Handler TypedContent
|
||||||
provide a = provideHtmlAndAP a $ redirectToPrettyJSON here
|
provide a = provideHtmlAndAP a $ redirectToPrettyJSON here
|
||||||
countTickets pid = fmap toOne $
|
countTickets pid = fmap toOne $
|
||||||
E.select $ E.from $ \ (tal `E.LeftOuterJoin` tup) -> do
|
E.select $ E.from $ \ (tal `E.InnerJoin` lt `E.LeftOuterJoin` tup `E.LeftOuterJoin` pt) -> do
|
||||||
|
E.on $ E.just (lt E.^. LocalTicketTicket) E.==. pt E.?. PatchTicket
|
||||||
E.on $ E.just (tal E.^. TicketAuthorLocalId) E.==. tup E.?. TicketUnderProjectAuthor
|
E.on $ E.just (tal E.^. TicketAuthorLocalId) E.==. tup E.?. TicketUnderProjectAuthor
|
||||||
|
E.on $ tal E.^. TicketAuthorLocalTicket E.==. lt E.^. LocalTicketId
|
||||||
E.where_ $
|
E.where_ $
|
||||||
tal E.^. TicketAuthorLocalAuthor E.==. E.val pid E.&&.
|
tal E.^. TicketAuthorLocalAuthor E.==. E.val pid E.&&.
|
||||||
E.isNothing (tup E.?. TicketUnderProjectId)
|
E.isNothing (tup E.?. TicketUnderProjectId) E.&&.
|
||||||
|
E.isNothing (pt E.?. PatchId)
|
||||||
return $ E.count $ tal E.^. TicketAuthorLocalId
|
return $ E.count $ tal E.^. TicketAuthorLocalId
|
||||||
where
|
where
|
||||||
toOne [x] = E.unValue x
|
toOne [x] = E.unValue x
|
||||||
toOne [] = error "toOne = 0"
|
toOne [] = error "toOne = 0"
|
||||||
toOne _ = error "toOne > 1"
|
toOne _ = error "toOne > 1"
|
||||||
selectTickets pid off lim =
|
selectTickets pid off lim =
|
||||||
E.select $ E.from $ \ (tal `E.LeftOuterJoin` tup) -> do
|
E.select $ E.from $ \ (tal `E.InnerJoin` lt `E.LeftOuterJoin` tup `E.LeftOuterJoin` pt) -> do
|
||||||
|
E.on $ E.just (lt E.^. LocalTicketTicket) E.==. pt E.?. PatchTicket
|
||||||
E.on $ E.just (tal E.^. TicketAuthorLocalId) E.==. tup E.?. TicketUnderProjectAuthor
|
E.on $ E.just (tal E.^. TicketAuthorLocalId) E.==. tup E.?. TicketUnderProjectAuthor
|
||||||
|
E.on $ tal E.^. TicketAuthorLocalTicket E.==. lt E.^. LocalTicketId
|
||||||
E.where_ $
|
E.where_ $
|
||||||
tal E.^. TicketAuthorLocalAuthor E.==. E.val pid E.&&.
|
tal E.^. TicketAuthorLocalAuthor E.==. E.val pid E.&&.
|
||||||
E.isNothing (tup E.?. TicketUnderProjectId)
|
E.isNothing (tup E.?. TicketUnderProjectId) E.&&.
|
||||||
|
E.isNothing (pt E.?. PatchId)
|
||||||
E.orderBy [E.desc $ tal E.^. TicketAuthorLocalId]
|
E.orderBy [E.desc $ tal E.^. TicketAuthorLocalId]
|
||||||
E.offset $ fromIntegral off
|
E.offset $ fromIntegral off
|
||||||
E.limit $ fromIntegral lim
|
E.limit $ fromIntegral lim
|
||||||
|
@ -1241,7 +1247,7 @@ getSharerTicketR shr talkhid = do
|
||||||
, AP.ticketParticipants =
|
, AP.ticketParticipants =
|
||||||
encodeRouteLocal $ SharerTicketFollowersR shr talkhid
|
encodeRouteLocal $ SharerTicketFollowersR shr talkhid
|
||||||
, AP.ticketTeam =
|
, AP.ticketTeam =
|
||||||
encodeRouteLocal $ SharerTicketTeamR shr talkhid
|
Just $ encodeRouteLocal $ SharerTicketTeamR shr talkhid
|
||||||
, AP.ticketEvents =
|
, AP.ticketEvents =
|
||||||
encodeRouteLocal $ SharerTicketEventsR shr talkhid
|
encodeRouteLocal $ SharerTicketEventsR shr talkhid
|
||||||
, AP.ticketDeps =
|
, AP.ticketDeps =
|
||||||
|
|
|
@ -1578,6 +1578,8 @@ changes hLocal ctx =
|
||||||
insertMany_ $ map makeTPL tcls
|
insertMany_ $ map makeTPL tcls
|
||||||
-- 248
|
-- 248
|
||||||
, removeField "TicketContextLocal" "project"
|
, removeField "TicketContextLocal" "project"
|
||||||
|
-- 249
|
||||||
|
, addEntities model_2020_05_17
|
||||||
]
|
]
|
||||||
|
|
||||||
migrateDB
|
migrateDB
|
||||||
|
|
|
@ -197,6 +197,7 @@ module Vervis.Migration.Model
|
||||||
, TicketContextLocal247
|
, TicketContextLocal247
|
||||||
, TicketContextLocal247Generic (..)
|
, TicketContextLocal247Generic (..)
|
||||||
, TicketProjectLocal247Generic (..)
|
, TicketProjectLocal247Generic (..)
|
||||||
|
, model_2020_05_17
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
|
@ -391,3 +392,6 @@ model_2020_05_16 = $(schema "2020_05_16_tcl")
|
||||||
|
|
||||||
makeEntitiesMigration "247"
|
makeEntitiesMigration "247"
|
||||||
$(modelFile "migrations/2020_05_16_tcl_mig.model")
|
$(modelFile "migrations/2020_05_16_tcl_mig.model")
|
||||||
|
|
||||||
|
model_2020_05_17 :: [Entity SqlBackend]
|
||||||
|
model_2020_05_17 = $(schema "2020_05_17_patch")
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{- This file is part of Vervis.
|
{- This file is part of Vervis.
|
||||||
-
|
-
|
||||||
- Written in 2018 by fr33domlover <fr33domlover@riseup.net>.
|
- Written in 2020 by fr33domlover <fr33domlover@riseup.net>.
|
||||||
-
|
-
|
||||||
- ♡ Copying is an act of love. Please copy, reuse and share.
|
- ♡ Copying is an act of love. Please copy, reuse and share.
|
||||||
-
|
-
|
||||||
|
@ -13,54 +13,163 @@
|
||||||
- <http://creativecommons.org/publicdomain/zero/1.0/>.
|
- <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
-}
|
-}
|
||||||
|
|
||||||
-- | Representation of a commit in a repo for viewing.
|
|
||||||
--
|
|
||||||
-- Each version control system has its own specific details of how repository
|
|
||||||
-- changes are represented and encoded and stored internally. This module is
|
|
||||||
-- merely a model for displaying a commit to a human viewer.
|
|
||||||
module Vervis.Patch
|
module Vervis.Patch
|
||||||
( Hunk (..)
|
( getSharerPatch
|
||||||
, Edit (..)
|
, getSharerPatch404
|
||||||
, Author (..)
|
, getRepoPatch
|
||||||
, Patch (..)
|
, getRepoPatch404
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
import Data.Int (Int64)
|
import Control.Monad
|
||||||
import Data.List.NonEmpty (NonEmpty)
|
import Control.Monad.Trans.Class
|
||||||
import Data.Text (Text)
|
import Control.Monad.Trans.Maybe
|
||||||
import Data.Time.Clock (UTCTime)
|
import Data.Maybe
|
||||||
import Data.Word (Word32)
|
import Data.Traversable
|
||||||
import Data.Vector (Vector)
|
import Database.Persist
|
||||||
import Text.Email.Validate (EmailAddress)
|
import Yesod.Core
|
||||||
|
|
||||||
data Hunk = Hunk
|
import Yesod.Hashids
|
||||||
{ hunkAddFirst :: [Text]
|
|
||||||
, hunkRemoveAdd :: [(NonEmpty Text, NonEmpty Text)]
|
|
||||||
, hunkRemoveLast :: [Text]
|
|
||||||
}
|
|
||||||
|
|
||||||
data Edit
|
import Data.Either.Local
|
||||||
= AddTextFile FilePath Word32 [Text]
|
import Database.Persist.Local
|
||||||
| AddBinaryFile FilePath Word32 Int64
|
|
||||||
| RemoveTextFile FilePath Word32 [Text]
|
|
||||||
| RemoveBinaryFile FilePath Word32 Int64
|
|
||||||
| MoveFile FilePath Word32 FilePath Word32
|
|
||||||
| ChmodFile FilePath Word32 Word32
|
|
||||||
| EditTextFile FilePath (Vector Text) (NonEmpty (Bool, Int, Hunk)) Word32 Word32
|
|
||||||
| EditBinaryFile FilePath Int64 Word32 Int64 Word32
|
|
||||||
| TextToBinary FilePath [Text] Word32 Int64 Word32
|
|
||||||
| BinaryToText FilePath Int64 Word32 [Text] Word32
|
|
||||||
|
|
||||||
data Author = Author
|
import Vervis.Foundation
|
||||||
{ authorName :: Text
|
import Vervis.Model
|
||||||
, authorEmail :: EmailAddress
|
import Vervis.Model.Ident
|
||||||
}
|
|
||||||
|
|
||||||
data Patch = Patch
|
getSharerPatch
|
||||||
{ patchWritten :: (Author, UTCTime)
|
:: ShrIdent
|
||||||
, patchCommitted :: Maybe (Author, UTCTime)
|
-> TicketAuthorLocalId
|
||||||
, patchTitle :: Text
|
-> AppDB
|
||||||
, patchDescription :: Text
|
( Maybe
|
||||||
, patchDiff :: [Edit]
|
( Entity TicketAuthorLocal
|
||||||
}
|
, Entity LocalTicket
|
||||||
|
, Entity Ticket
|
||||||
|
, Either
|
||||||
|
( Entity TicketContextLocal
|
||||||
|
, Entity TicketRepoLocal
|
||||||
|
)
|
||||||
|
( Entity TicketProjectRemote
|
||||||
|
, Maybe (Entity TicketProjectRemoteAccept)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
getSharerPatch shr talid = runMaybeT $ do
|
||||||
|
pid <- do
|
||||||
|
sid <- MaybeT $ getKeyBy $ UniqueSharer shr
|
||||||
|
MaybeT $ getKeyBy $ UniquePersonIdent sid
|
||||||
|
tal <- MaybeT $ get talid
|
||||||
|
guard $ ticketAuthorLocalAuthor tal == pid
|
||||||
|
let ltid = ticketAuthorLocalTicket tal
|
||||||
|
lt <- lift $ getJust ltid
|
||||||
|
let tid = localTicketTicket lt
|
||||||
|
t <- lift $ getJust tid
|
||||||
|
npatches <- lift $ count [PatchTicket ==. tid]
|
||||||
|
guard $ npatches >= 1
|
||||||
|
repo <-
|
||||||
|
requireEitherAlt
|
||||||
|
(do mtcl <- lift $ getBy $ UniqueTicketContextLocal tid
|
||||||
|
for mtcl $ \ etcl@(Entity tclid tcl) -> do
|
||||||
|
etrl <- MaybeT $ getBy $ UniqueTicketRepoLocal tclid
|
||||||
|
mtup1 <- lift $ getBy $ UniqueTicketUnderProjectProject tclid
|
||||||
|
mtup2 <- lift $ getBy $ UniqueTicketUnderProjectAuthor talid
|
||||||
|
unless (isJust mtup1 == isJust mtup2) $
|
||||||
|
error "TUP points to unrelated TAL and TCL!"
|
||||||
|
guard $ not $ isJust mtup1
|
||||||
|
return (etcl, etrl)
|
||||||
|
)
|
||||||
|
(do mtpr <- lift $ getBy $ UniqueTicketProjectRemote talid
|
||||||
|
lift $ for mtpr $ \ etpr@(Entity tprid _) ->
|
||||||
|
(etpr,) <$> getBy (UniqueTicketProjectRemoteAccept tprid)
|
||||||
|
)
|
||||||
|
"MR doesn't have context"
|
||||||
|
"MR has both local and remote context"
|
||||||
|
return (Entity talid tal, Entity ltid lt, Entity tid t, repo)
|
||||||
|
|
||||||
|
getSharerPatch404
|
||||||
|
:: ShrIdent
|
||||||
|
-> KeyHashid TicketAuthorLocal
|
||||||
|
-> AppDB
|
||||||
|
( Entity TicketAuthorLocal
|
||||||
|
, Entity LocalTicket
|
||||||
|
, Entity Ticket
|
||||||
|
, Either
|
||||||
|
( Entity TicketContextLocal
|
||||||
|
, Entity TicketRepoLocal
|
||||||
|
)
|
||||||
|
( Entity TicketProjectRemote
|
||||||
|
, Maybe (Entity TicketProjectRemoteAccept)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
getSharerPatch404 shr talkhid = do
|
||||||
|
talid <- decodeKeyHashid404 talkhid
|
||||||
|
mpatch <- getSharerPatch shr talid
|
||||||
|
case mpatch of
|
||||||
|
Nothing -> notFound
|
||||||
|
Just patch -> return patch
|
||||||
|
|
||||||
|
getRepoPatch
|
||||||
|
:: ShrIdent
|
||||||
|
-> RpIdent
|
||||||
|
-> LocalTicketId
|
||||||
|
-> AppDB
|
||||||
|
( Maybe
|
||||||
|
( Entity Sharer
|
||||||
|
, Entity Repo
|
||||||
|
, Entity Ticket
|
||||||
|
, Entity LocalTicket
|
||||||
|
, Entity TicketContextLocal
|
||||||
|
, Entity TicketRepoLocal
|
||||||
|
, Either
|
||||||
|
(Entity TicketAuthorLocal, Entity TicketUnderProject)
|
||||||
|
(Entity TicketAuthorRemote)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
getRepoPatch shr rp ltid = runMaybeT $ do
|
||||||
|
es@(Entity sid _) <- MaybeT $ getBy $ UniqueSharer shr
|
||||||
|
er@(Entity rid _) <- MaybeT $ getBy $ UniqueRepo rp sid
|
||||||
|
lt <- MaybeT $ get ltid
|
||||||
|
let tid = localTicketTicket lt
|
||||||
|
t <- MaybeT $ get tid
|
||||||
|
etcl@(Entity tclid _) <- MaybeT $ getBy $ UniqueTicketContextLocal tid
|
||||||
|
etrl@(Entity _ trl) <- MaybeT $ getBy $ UniqueTicketRepoLocal tclid
|
||||||
|
guard $ ticketRepoLocalRepo trl == rid
|
||||||
|
npatches <- lift $ count [PatchTicket ==. tid]
|
||||||
|
guard $ npatches >= 1
|
||||||
|
author <-
|
||||||
|
requireEitherAlt
|
||||||
|
(do mtal <- lift $ getBy $ UniqueTicketAuthorLocal ltid
|
||||||
|
for mtal $ \ tal@(Entity talid _) -> do
|
||||||
|
tupid1 <- MaybeT $ getKeyBy $ UniqueTicketUnderProjectProject tclid
|
||||||
|
tup@(Entity tupid2 _) <- MaybeT $ getBy $ UniqueTicketUnderProjectAuthor talid
|
||||||
|
unless (tupid1 == tupid2) $
|
||||||
|
error "TAL and TPL used by different TUPs!"
|
||||||
|
return (tal, tup)
|
||||||
|
)
|
||||||
|
(lift $ getBy $ UniqueTicketAuthorRemote tclid)
|
||||||
|
"MR doesn't have author"
|
||||||
|
"MR has both local and remote author"
|
||||||
|
return (es, er, Entity tid t, Entity ltid lt, etcl, etrl, author)
|
||||||
|
|
||||||
|
getRepoPatch404
|
||||||
|
:: ShrIdent
|
||||||
|
-> RpIdent
|
||||||
|
-> KeyHashid LocalTicket
|
||||||
|
-> AppDB
|
||||||
|
( Entity Sharer
|
||||||
|
, Entity Repo
|
||||||
|
, Entity Ticket
|
||||||
|
, Entity LocalTicket
|
||||||
|
, Entity TicketContextLocal
|
||||||
|
, Entity TicketRepoLocal
|
||||||
|
, Either
|
||||||
|
(Entity TicketAuthorLocal, Entity TicketUnderProject)
|
||||||
|
(Entity TicketAuthorRemote)
|
||||||
|
)
|
||||||
|
getRepoPatch404 shr rp ltkhid = do
|
||||||
|
ltid <- decodeKeyHashid404 ltkhid
|
||||||
|
mpatch <- getRepoPatch shr rp ltid
|
||||||
|
case mpatch of
|
||||||
|
Nothing -> notFound
|
||||||
|
Just patch -> return patch
|
||||||
|
|
|
@ -46,6 +46,8 @@ import Data.Traversable
|
||||||
import Database.Esqueleto
|
import Database.Esqueleto
|
||||||
import Yesod.Core (notFound)
|
import Yesod.Core (notFound)
|
||||||
|
|
||||||
|
import qualified Database.Persist as P
|
||||||
|
|
||||||
import Yesod.Hashids
|
import Yesod.Hashids
|
||||||
|
|
||||||
import Data.Either.Local
|
import Data.Either.Local
|
||||||
|
@ -457,6 +459,8 @@ getSharerTicket shr talid = runMaybeT $ do
|
||||||
lt <- lift $ getJust ltid
|
lt <- lift $ getJust ltid
|
||||||
let tid = localTicketTicket lt
|
let tid = localTicketTicket lt
|
||||||
t <- lift $ getJust tid
|
t <- lift $ getJust tid
|
||||||
|
npatches <- lift $ P.count [PatchTicket P.==. tid]
|
||||||
|
guard $ npatches <= 0
|
||||||
project <-
|
project <-
|
||||||
requireEitherAlt
|
requireEitherAlt
|
||||||
(do mtcl <- lift $ getBy $ UniqueTicketContextLocal tid
|
(do mtcl <- lift $ getBy $ UniqueTicketContextLocal tid
|
||||||
|
@ -525,6 +529,8 @@ getProjectTicket shr prj ltid = runMaybeT $ do
|
||||||
etcl@(Entity tclid _) <- MaybeT $ getBy $ UniqueTicketContextLocal tid
|
etcl@(Entity tclid _) <- MaybeT $ getBy $ UniqueTicketContextLocal tid
|
||||||
etpl@(Entity _ tpl) <- MaybeT $ getBy $ UniqueTicketProjectLocal tclid
|
etpl@(Entity _ tpl) <- MaybeT $ getBy $ UniqueTicketProjectLocal tclid
|
||||||
guard $ ticketProjectLocalProject tpl == jid
|
guard $ ticketProjectLocalProject tpl == jid
|
||||||
|
npatches <- lift $ P.count [PatchTicket P.==. tid]
|
||||||
|
guard $ npatches <= 0
|
||||||
author <-
|
author <-
|
||||||
requireEitherAlt
|
requireEitherAlt
|
||||||
(do mtal <- lift $ getBy $ UniqueTicketAuthorLocal ltid
|
(do mtal <- lift $ getBy $ UniqueTicketAuthorLocal ltid
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{- This file is part of Vervis.
|
{- This file is part of Vervis.
|
||||||
-
|
-
|
||||||
- Written in 2016, 2018 by fr33domlover <fr33domlover@riseup.net>.
|
- Written in 2016, 2018, 2020 by fr33domlover <fr33domlover@riseup.net>.
|
||||||
-
|
-
|
||||||
- ♡ Copying is an act of love. Please copy, reuse and share.
|
- ♡ Copying is an act of love. Please copy, reuse and share.
|
||||||
-
|
-
|
||||||
|
@ -31,10 +31,11 @@ import qualified Data.List.NonEmpty as N
|
||||||
import qualified Data.Text as T (take)
|
import qualified Data.Text as T (take)
|
||||||
import qualified Data.Vector as V
|
import qualified Data.Vector as V
|
||||||
|
|
||||||
|
import Data.Patch.Local (Hunk (..))
|
||||||
|
|
||||||
import Vervis.Changes
|
import Vervis.Changes
|
||||||
import Vervis.Foundation
|
import Vervis.Foundation
|
||||||
import Vervis.Model.Ident
|
import Vervis.Model.Ident
|
||||||
import Vervis.Patch (Hunk (..))
|
|
||||||
import Vervis.Settings (widgetFile, appDiffContextLines)
|
import Vervis.Settings (widgetFile, appDiffContextLines)
|
||||||
import Vervis.Style
|
import Vervis.Style
|
||||||
|
|
||||||
|
|
|
@ -827,7 +827,7 @@ data TicketLocal = TicketLocal
|
||||||
{ ticketId :: LocalURI
|
{ ticketId :: LocalURI
|
||||||
, ticketReplies :: LocalURI
|
, ticketReplies :: LocalURI
|
||||||
, ticketParticipants :: LocalURI
|
, ticketParticipants :: LocalURI
|
||||||
, ticketTeam :: LocalURI
|
, ticketTeam :: Maybe LocalURI
|
||||||
, ticketEvents :: LocalURI
|
, ticketEvents :: LocalURI
|
||||||
, ticketDeps :: LocalURI
|
, ticketDeps :: LocalURI
|
||||||
, ticketReverseDeps :: LocalURI
|
, ticketReverseDeps :: LocalURI
|
||||||
|
@ -851,7 +851,7 @@ parseTicketLocal o = do
|
||||||
<$> pure id_
|
<$> pure id_
|
||||||
<*> withAuthorityO a (o .: "replies")
|
<*> withAuthorityO a (o .: "replies")
|
||||||
<*> withAuthorityO a (o .: "participants")
|
<*> withAuthorityO a (o .: "participants")
|
||||||
<*> withAuthorityO a (o .: "team")
|
<*> withAuthorityMaybeO a (o .:? "team")
|
||||||
<*> withAuthorityO a (o .: "history")
|
<*> withAuthorityO a (o .: "history")
|
||||||
<*> withAuthorityO a (o .: "dependencies")
|
<*> withAuthorityO a (o .: "dependencies")
|
||||||
<*> withAuthorityO a (o .: "dependants")
|
<*> withAuthorityO a (o .: "dependants")
|
||||||
|
@ -867,7 +867,7 @@ encodeTicketLocal
|
||||||
= "id" .= ObjURI a id_
|
= "id" .= ObjURI a id_
|
||||||
<> "replies" .= ObjURI a replies
|
<> "replies" .= ObjURI a replies
|
||||||
<> "participants" .= ObjURI a participants
|
<> "participants" .= ObjURI a participants
|
||||||
<> "team" .= ObjURI a team
|
<> "team" .=? (ObjURI a <$> team)
|
||||||
<> "history" .= ObjURI a events
|
<> "history" .= ObjURI a events
|
||||||
<> "dependencies" .= ObjURI a deps
|
<> "dependencies" .= ObjURI a deps
|
||||||
<> "dependants" .= ObjURI a rdeps
|
<> "dependants" .= ObjURI a rdeps
|
||||||
|
|
|
@ -73,6 +73,7 @@ library
|
||||||
Data.Maybe.Local
|
Data.Maybe.Local
|
||||||
Data.MediaType
|
Data.MediaType
|
||||||
Data.Paginate.Local
|
Data.Paginate.Local
|
||||||
|
Data.Patch.Local
|
||||||
Data.Text.UTF8.Local
|
Data.Text.UTF8.Local
|
||||||
Data.Text.Lazy.UTF8.Local
|
Data.Text.Lazy.UTF8.Local
|
||||||
Data.Time.Clock.Local
|
Data.Time.Clock.Local
|
||||||
|
@ -163,6 +164,7 @@ library
|
||||||
Vervis.Handler.Home
|
Vervis.Handler.Home
|
||||||
Vervis.Handler.Inbox
|
Vervis.Handler.Inbox
|
||||||
Vervis.Handler.Key
|
Vervis.Handler.Key
|
||||||
|
Vervis.Handler.Patch
|
||||||
Vervis.Handler.Person
|
Vervis.Handler.Person
|
||||||
Vervis.Handler.Project
|
Vervis.Handler.Project
|
||||||
Vervis.Handler.Repo
|
Vervis.Handler.Repo
|
||||||
|
@ -189,8 +191,8 @@ library
|
||||||
Vervis.Model.Workflow
|
Vervis.Model.Workflow
|
||||||
Vervis.Paginate
|
Vervis.Paginate
|
||||||
Vervis.Palette
|
Vervis.Palette
|
||||||
Vervis.Path
|
|
||||||
Vervis.Patch
|
Vervis.Patch
|
||||||
|
Vervis.Path
|
||||||
Vervis.Query
|
Vervis.Query
|
||||||
Vervis.Readme
|
Vervis.Readme
|
||||||
Vervis.RemoteActorStore
|
Vervis.RemoteActorStore
|
||||||
|
|
Loading…
Reference in a new issue