S2S unresolve, C2S resolve & unresolve, use C2S in the UI buttons

This commit is contained in:
fr33domlover 2020-08-05 08:28:58 +00:00
parent 7f106023b0
commit 9317e514b2
14 changed files with 897 additions and 350 deletions

View file

@ -368,8 +368,6 @@ Ticket
description Text -- HTML
assignee PersonId Maybe
status TicketStatus
closed UTCTime
closer PersonId Maybe
-- UniqueTicket project number

View file

@ -20,6 +20,7 @@ module Vervis.API
, followC
, offerTicketC
, offerDepC
, resolveC
, undoC
, pushCommitsC
, getFollowersCollection
@ -855,8 +856,6 @@ createTicketC (Entity pidUser personUser) sharerUser summary audience ticket muT
, ticketDescription = unTextHtml desc
, ticketAssignee = Nothing
, ticketStatus = TSNew
, ticketClosed = UTCTime (ModifiedJulianDay 0) 0
, ticketCloser = Nothing
}
ltid <- insert LocalTicket
{ localTicketTicket = tid
@ -1514,8 +1513,6 @@ offerTicketC (Entity pidUser personUser) sharerUser summary audience ticket uTar
, ticketDescription = unTextHtml desc
, ticketAssignee = Nothing
, ticketStatus = TSNew
, ticketClosed = UTCTime (ModifiedJulianDay 0) 0
, ticketCloser = Nothing
}
ltid <- insert LocalTicket
{ localTicketTicket = tid
@ -1585,6 +1582,59 @@ offerTicketC (Entity pidUser personUser) sharerUser summary audience ticket uTar
update obiidAccept [OutboxItemActivity =. persistJSONObjectFromDoc doc]
return (doc, makeRecipientSet actors collections)
verifyHosterRecip _ _ (Right _) = return ()
verifyHosterRecip localRecips name (Left wi) =
fromMaybeE (verify wi) $
name <> " ticket hoster actor isn't listed as a recipient"
where
verify (WorkItemSharerTicket shr _ _) = do
sharerSet <- lookup shr localRecips
guard $ localRecipSharer $ localRecipSharerDirect sharerSet
verify (WorkItemProjectTicket shr prj _) = do
sharerSet <- lookup shr localRecips
projectSet <- lookup prj $ localRecipProjectRelated sharerSet
guard $ localRecipProject $ localRecipProjectDirect projectSet
verify (WorkItemRepoPatch shr rp _) = do
sharerSet <- lookup shr localRecips
repoSet <- lookup rp $ localRecipRepoRelated sharerSet
guard $ localRecipRepo $ localRecipRepoDirect repoSet
workItemRecipSieve wiFollowers (WorkItemDetail ident context author) =
let authorC =
case author of
Left shr -> [LocalPersonCollectionSharerFollowers shr]
Right _ -> []
ticketC =
case ident of
Left (wi, _) -> [wiFollowers wi]
Right _ -> []
(contextA, contextC) =
case context of
Left local ->
case local of
Left (shr, prj) ->
( [LocalActorProject shr prj]
, [ LocalPersonCollectionProjectTeam shr prj
, LocalPersonCollectionProjectFollowers shr prj
]
)
Right (shr, rp) ->
( [LocalActorRepo shr rp]
, [ LocalPersonCollectionRepoTeam shr rp
, LocalPersonCollectionRepoFollowers shr rp
]
)
Right _ -> ([], [])
in (contextA, authorC ++ ticketC ++ contextC)
workItemActor (WorkItemSharerTicket shr _ _) = LocalActorSharer shr
workItemActor (WorkItemProjectTicket shr prj _) = LocalActorProject shr prj
workItemActor (WorkItemRepoPatch shr rp _) = LocalActorRepo shr rp
actorOutboxItem (LocalActorSharer shr) = SharerOutboxItemR shr
actorOutboxItem (LocalActorProject shr prj) = ProjectOutboxItemR shr prj
actorOutboxItem (LocalActorRepo shr rp) = RepoOutboxItemR shr rp
offerDepC
:: Entity Person
-> Sharer
@ -1698,25 +1748,6 @@ offerDepC (Entity pidUser personUser) sharerUser summary audience dep uTarget =
forkWorker "offerDepC: async HTTP Accept delivery" $ deliverRemoteHttp' fwdHostsAccept obiidAccept docAccept remotesHttpAccept
return obiidOffer
where
runWorkerExcept action = do
site <- askSite
ExceptT $ liftIO $ runWorker (runExceptT action) site
verifyHosterRecip _ _ (Right _) = return ()
verifyHosterRecip localRecips name (Left wi) =
fromMaybeE (verify wi) $
name <> " ticket hoster actor isn't listed as a recipient"
where
verify (WorkItemSharerTicket shr _ _) = do
sharerSet <- lookup shr localRecips
guard $ localRecipSharer $ localRecipSharerDirect sharerSet
verify (WorkItemProjectTicket shr prj _) = do
sharerSet <- lookup shr localRecips
projectSet <- lookup prj $ localRecipProjectRelated sharerSet
guard $ localRecipProject $ localRecipProjectDirect projectSet
verify (WorkItemRepoPatch shr rp _) = do
sharerSet <- lookup shr localRecips
repoSet <- lookup rp $ localRecipRepoRelated sharerSet
guard $ localRecipRepo $ localRecipRepoDirect repoSet
insertOfferToOutbox shrUser now obid blinded = do
hLocal <- asksSite siteInstanceHost
obiid <- insertEmptyOutboxItem obid now
@ -1733,33 +1764,6 @@ offerDepC (Entity pidUser personUser) sharerUser summary audience dep uTarget =
}
update obiid [OutboxItemActivity =. persistJSONObjectFromDoc doc]
return (obiid, doc, luAct)
workItemRecipSieve wiFollowers (WorkItemDetail ident context author) =
let authorC =
case author of
Left shr -> [LocalPersonCollectionSharerFollowers shr]
Right _ -> []
ticketC =
case ident of
Left (wi, _) -> [wiFollowers wi]
Right _ -> []
(contextA, contextC) =
case context of
Left local ->
case local of
Left (shr, prj) ->
( [LocalActorProject shr prj]
, [ LocalPersonCollectionProjectTeam shr prj
, LocalPersonCollectionProjectFollowers shr prj
]
)
Right (shr, rp) ->
( [LocalActorRepo shr rp]
, [ LocalPersonCollectionRepoTeam shr rp
, LocalPersonCollectionRepoFollowers shr rp
]
)
Right _ -> ([], [])
in (contextA, authorC ++ ticketC ++ contextC)
insertDep now pidAuthor obiidOffer ltidParent child obiidAccept = do
tdid <- insert LocalTicketDependency
{ localTicketDependencyParent = ltidParent
@ -1839,91 +1843,306 @@ offerDepC (Entity pidUser personUser) sharerUser summary audience dep uTarget =
update obiidAccept [OutboxItemActivity =. persistJSONObjectFromDoc doc]
return (doc, recipientSet, remoteActors, fwdHosts)
where
authorAudience (Left shr) = AudLocal [LocalActorSharer shr] []
authorAudience (Right (ObjURI h lu)) = AudRemote h [lu] []
actorOutboxItem (LocalActorSharer shr) = SharerOutboxItemR shr
actorOutboxItem (LocalActorProject shr prj) = ProjectOutboxItemR shr prj
actorOutboxItem (LocalActorRepo shr rp) = RepoOutboxItemR shr rp
insertAcceptOnTicketStatus shrUser wi (WorkItemDetail _ ctx author) obiidResolve obiidAccept = do
encodeRouteLocal <- getEncodeRouteLocal
encodeRouteHome <- getEncodeRouteHome
wiFollowers <- askWorkItemFollowers
hLocal <- asksSite siteInstanceHost
obikhidResolve <- encodeKeyHashid obiidResolve
obikhidAccept <- encodeKeyHashid obiidAccept
let audAuthor =
AudLocal
[LocalActorSharer shrUser]
[LocalPersonCollectionSharerFollowers shrUser]
audTicketContext = contextAudience ctx
audTicketAuthor = authorAudience author
audTicketFollowers = AudLocal [] [wiFollowers wi]
(recipientSet, remoteActors, fwdHosts, audLocal, audRemote) =
collectAudience $
audAuthor :
audTicketAuthor :
audTicketFollowers :
audTicketContext
actor = workItemActor wi
recips = map encodeRouteHome audLocal ++ audRemote
doc = Doc hLocal Activity
{ activityId =
Just $ encodeRouteLocal $
actorOutboxItem actor obikhidAccept
, activityActor = encodeRouteLocal $ renderLocalActor actor
, activitySummary = Nothing
, activityAudience = Audience recips [] [] [] [] []
, activitySpecific = AcceptActivity Accept
{ acceptObject =
encodeRouteHome $ SharerOutboxItemR shrUser obikhidResolve
, acceptResult = Nothing
}
}
update obiidAccept [OutboxItemActivity =. persistJSONObjectFromDoc doc]
return (doc, recipientSet, remoteActors, fwdHosts)
resolveC
:: Entity Person
-> Sharer
-> Maybe TextHtml
-> Audience URIMode
-> Resolve URIMode
-> ExceptT Text Handler OutboxItemId
resolveC (Entity pidUser personUser) sharerUser summary audience (Resolve uObject) = do
let shrUser = sharerIdent sharerUser
object <- parseWorkItem "Resolve object" uObject
ParsedAudience localRecips remoteRecips blinded fwdHosts <- do
mrecips <- parseAudience audience
fromMaybeE mrecips "Offer Ticket with no recipients"
federation <- asksSite $ appFederation . appSettings
unless (federation || null remoteRecips) $
throwE "Federation disabled, but remote recipients specified"
verifyHosterRecip localRecips "Parent" object
now <- liftIO getCurrentTime
ticketDetail <- runWorkerExcept $ getWorkItemDetail "Object" object
(obiid, doc, remotesHttp, maybeAccept) <- runDBExcept $ do
(obiidResolve, docResolve, luResolve) <- lift $ insertResolveToOutbox shrUser now (personOutbox personUser) blinded
remotesHttpResolve <- do
wiFollowers <- askWorkItemFollowers
let sieve =
let (actors, colls) =
workItemRecipSieve wiFollowers ticketDetail
in makeRecipientSet
actors
(LocalPersonCollectionSharerFollowers shrUser :
colls
)
moreRemoteRecips <-
lift $
deliverLocal'
True
(LocalActorSharer shrUser)
(personInbox personUser)
obiidResolve
(localRecipSieve sieve False localRecips)
unless (federation || null moreRemoteRecips) $
throwE "Federation disabled, but recipient collection remote members found"
lift $ deliverRemoteDB'' fwdHosts obiidResolve remoteRecips moreRemoteRecips
maccept <-
case widIdent ticketDetail of
Right _ -> return Nothing
Left (wi, ltid) -> Just <$> do
mhoster <-
lift $ runMaybeT $
case wi of
WorkItemSharerTicket shr _ _ -> do
sid <- MaybeT $ getKeyBy $ UniqueSharer shr
p <- MaybeT (getValBy $ UniquePersonIdent sid)
return (personOutbox p, personInbox p)
WorkItemProjectTicket shr prj _ -> do
sid <- MaybeT $ getKeyBy $ UniqueSharer shr
j <- MaybeT (getValBy $ UniqueProject prj sid)
return (projectOutbox j, projectInbox j)
WorkItemRepoPatch shr rp _ -> do
sid <- MaybeT $ getKeyBy $ UniqueSharer shr
r <- MaybeT (getValBy $ UniqueRepo rp sid)
return (repoOutbox r, repoInbox r)
(obidHoster, ibidHoster) <- fromMaybeE mhoster "Ticket hoster not in DB"
obiidAccept <- lift $ insertEmptyOutboxItem obidHoster now
lift $ insertResolve ltid obiidResolve obiidAccept
(docAccept, localRecipsAccept, remoteRecipsAccept, fwdHostsAccept) <-
lift $ insertAcceptOnTicketStatus shrUser wi ticketDetail obiidResolve obiidAccept
knownRemoteRecipsAccept <-
lift $
deliverLocal'
False
(workItemActor wi)
ibidHoster
obiidAccept
localRecipsAccept
lift $ (obiidAccept,docAccept,fwdHostsAccept,) <$>
deliverRemoteDB'' fwdHostsAccept obiidAccept remoteRecipsAccept knownRemoteRecipsAccept
return (obiidResolve, docResolve, remotesHttpResolve, maccept)
lift $ do
forkWorker "resolveC: async HTTP Resolve delivery" $ deliverRemoteHttp' fwdHosts obiid doc remotesHttp
for_ maybeAccept $ \ (obiidAccept, docAccept, fwdHostsAccept, remotesHttpAccept) ->
forkWorker "resolveC: async HTTP Accept delivery" $ deliverRemoteHttp' fwdHostsAccept obiidAccept docAccept remotesHttpAccept
return obiid
where
insertResolveToOutbox shrUser now obid blinded = do
hLocal <- asksSite siteInstanceHost
obiid <- insertEmptyOutboxItem obid now
encodeRouteLocal <- getEncodeRouteLocal
obikhid <- encodeKeyHashid obiid
let luAct = encodeRouteLocal $ SharerOutboxItemR shrUser obikhid
doc = Doc hLocal Activity
{ activityId = Just luAct
, activityActor = encodeRouteLocal $ SharerR shrUser
, activitySummary = summary
, activityAudience = blinded
, activitySpecific = ResolveActivity $ Resolve uObject
}
update obiid [OutboxItemActivity =. persistJSONObjectFromDoc doc]
return (obiid, doc, luAct)
insertResolve ltid obiidResolve obiidAccept = do
trid <- insert TicketResolve
{ ticketResolveTicket = ltid
, ticketResolveAccept = obiidAccept
}
insert_ TicketResolveLocal
{ ticketResolveLocalTicket = trid
, ticketResolveLocalActivity = obiidResolve
}
tid <- localTicketTicket <$> getJust ltid
update tid [TicketStatus =. TSClosed]
undoC
:: ShrIdent
:: Entity Person
-> Sharer
-> Maybe TextHtml
-> Audience URIMode
-> Undo URIMode
-> ExceptT Text Handler OutboxItemId
undoC shrUser summary audience undo@(Undo luObject) = do
undoC (Entity _pidUser personUser) sharerUser summary audience undo@(Undo uObject) = do
let shrUser = sharerIdent sharerUser
object <- parseActivity uObject
ParsedAudience localRecips remoteRecips blinded fwdHosts <- do
mrecips <- parseAudience audience
fromMaybeE mrecips "Follow with no recipients"
fromMaybeE mrecips "Undo with no recipients"
federation <- asksSite $ appFederation . appSettings
unless (federation || null remoteRecips) $
throwE "Federation disabled, but remote recipients specified"
route <-
fromMaybeE
(decodeRouteLocal luObject)
"Undo object isn't a valid route"
obiidOriginal <- case route of
SharerOutboxItemR shr obikhid
| shr == shrUser ->
decodeKeyHashidE obikhid "Undo object invalid obikhid"
_ -> throwE "Undo object isn't actor's outbox item route"
(obiidUndo, doc, remotesHttp) <- runDBExcept $ do
Entity _pidAuthor personAuthor <- lift $ getAuthor shrUser
obi <- do
mobi <- lift $ get obiidOriginal
fromMaybeE mobi "Undo object obiid doesn't exist in DB"
unless (outboxItemOutbox obi == personOutbox personAuthor) $
throwE "Undo object obiid belongs to different actor"
lift $ do
deleteFollow obiidOriginal
deleteFollowRemote obiidOriginal
deleteFollowRemoteRequest obiidOriginal
let obidAuthor = personOutbox personAuthor
(obiidUndo, doc, luUndo) <- insertUndoToOutbox obidAuthor blinded
let ibidAuthor = personInbox personAuthor
fsidAuthor = personFollowers personAuthor
knownRemotes <- deliverLocal shrUser ibidAuthor fsidAuthor obiidUndo localRecips
remotesHttp <- deliverRemoteDB'' fwdHosts obiidUndo remoteRecips knownRemotes
return (obiidUndo, doc, remotesHttp)
lift $ forkWorker "undoC: Outbox POST handler: async HTTP delivery" $ deliverRemoteHttp' fwdHosts obiidUndo doc remotesHttp
return obiidUndo
now <- liftIO getCurrentTime
(obiid, doc, _lu, mwi) <- runDBExcept $ do
(obiidUndo, docUndo, luUndo) <- lift $ insertUndoToOutbox shrUser now (personOutbox personUser) blinded
mltid <- fmap join $ runMaybeT $ do
object' <- MaybeT $ getActivity object
deleteFollow shrUser object' <|> deleteResolve object'
mwi <- lift $ traverse getWorkItem mltid
return (obiidUndo, docUndo, luUndo, mwi)
mticketDetail <-
for mwi $ \ wi ->
(wi,) <$> runWorkerExcept (getWorkItemDetail "Object" $ Left wi)
wiFollowers <- askWorkItemFollowers
let sieve =
case mticketDetail of
Nothing -> makeRecipientSet [] [LocalPersonCollectionSharerFollowers shrUser]
Just (_wi, ticketDetail) ->
let (actors, colls) =
workItemRecipSieve wiFollowers ticketDetail
in makeRecipientSet
actors
(LocalPersonCollectionSharerFollowers shrUser :
colls
)
(remotes, maybeAccept) <- runDBExcept $ do
remotesHttpUndo <- do
moreRemoteRecips <-
lift $
deliverLocal'
True
(LocalActorSharer shrUser)
(personInbox personUser)
obiid
(localRecipSieve sieve True localRecips)
unless (federation || null moreRemoteRecips) $
throwE "Federation disabled, but recipient collection remote members found"
lift $ deliverRemoteDB'' fwdHosts obiid remoteRecips moreRemoteRecips
maccept <- for mticketDetail $ \ (wi, ticketDetail) -> do
mhoster <-
lift $ runMaybeT $
case wi of
WorkItemSharerTicket shr _ _ -> do
sid <- MaybeT $ getKeyBy $ UniqueSharer shr
p <- MaybeT (getValBy $ UniquePersonIdent sid)
return (personOutbox p, personInbox p)
WorkItemProjectTicket shr prj _ -> do
sid <- MaybeT $ getKeyBy $ UniqueSharer shr
j <- MaybeT (getValBy $ UniqueProject prj sid)
return (projectOutbox j, projectInbox j)
WorkItemRepoPatch shr rp _ -> do
sid <- MaybeT $ getKeyBy $ UniqueSharer shr
r <- MaybeT (getValBy $ UniqueRepo rp sid)
return (repoOutbox r, repoInbox r)
(obidHoster, ibidHoster) <- fromMaybeE mhoster "Ticket hoster not in DB"
obiidAccept <- lift $ insertEmptyOutboxItem obidHoster now
(docAccept, localRecipsAccept, remoteRecipsAccept, fwdHostsAccept) <-
lift $ insertAcceptOnTicketStatus shrUser wi ticketDetail obiid obiidAccept
knownRemoteRecipsAccept <-
lift $
deliverLocal'
False
(workItemActor wi)
ibidHoster
obiidAccept
localRecipsAccept
lift $ (obiidAccept,docAccept,fwdHostsAccept,) <$>
deliverRemoteDB'' fwdHostsAccept obiidAccept remoteRecipsAccept knownRemoteRecipsAccept
return (remotesHttpUndo, maccept)
lift $ do
forkWorker "undoC: async HTTP Undo delivery" $
deliverRemoteHttp' fwdHosts obiid doc remotes
for_ maybeAccept $ \ (obiidAccept, docAccept, fwdHostsAccept, remotesHttpAccept) ->
forkWorker "undoC: async HTTP Accept delivery" $
deliverRemoteHttp' fwdHostsAccept obiidAccept docAccept remotesHttpAccept
return obiid
where
getAuthor shr = do
sid <- getKeyBy404 $ UniqueSharer shr
getBy404 $ UniquePersonIdent sid
deleteFollow obiid = do
mfid <- getKeyBy $ UniqueFollowFollow obiid
traverse_ delete mfid
deleteFollowRemote obiid = do
mfrid <- getKeyBy $ UniqueFollowRemoteFollow obiid
traverse_ delete mfrid
deleteFollowRemoteRequest obiid = do
mfrrid <- getKeyBy $ UniqueFollowRemoteRequestActivity obiid
traverse_ delete mfrrid
insertUndoToOutbox obid blinded = do
insertUndoToOutbox shrUser now obid blinded = do
hLocal <- asksSite siteInstanceHost
obiid <- insertEmptyOutboxItem obid now
encodeRouteLocal <- getEncodeRouteLocal
let activity mluAct = Doc hLocal Activity
{ activityId = mluAct
obikhid <- encodeKeyHashid obiid
let luAct = encodeRouteLocal $ SharerOutboxItemR shrUser obikhid
doc = Doc hLocal Activity
{ activityId = Just luAct
, activityActor = encodeRouteLocal $ SharerR shrUser
, activitySummary = summary
, activityAudience = blinded
, activitySpecific = UndoActivity undo
, activitySpecific = UndoActivity $ Undo uObject
}
now <- liftIO getCurrentTime
obiid <- insert OutboxItem
{ outboxItemOutbox = obid
, outboxItemActivity =
persistJSONObjectFromDoc $ activity Nothing
, outboxItemPublished = now
}
obikhid <- encodeKeyHashid obiid
let luAct = encodeRouteLocal $ SharerOutboxItemR shrUser obikhid
doc = activity $ Just luAct
update obiid [OutboxItemActivity =. persistJSONObjectFromDoc doc]
return (obiid, doc, luAct)
deleteFollow shr (Left (actor, obiid)) = do
deleteFollowLocal <|> deleteFollowRemote <|> deleteFollowRequest
return Nothing
where
deleteFollowLocal = do
fid <- MaybeT $ lift $ getKeyBy $ UniqueFollowFollow obiid
unless (actor == LocalActorSharer shr) $
lift $ throwE "Undoing someone else's follow"
lift $ lift $ delete fid
deleteFollowRemote = do
frid <- MaybeT $ lift $ getKeyBy $ UniqueFollowRemoteFollow obiid
unless (actor == LocalActorSharer shr) $
lift $ throwE "Undoing someone else's follow"
lift $ lift $ delete frid
deleteFollowRequest = do
frrid <- MaybeT $ lift $ getKeyBy $ UniqueFollowRemoteRequestActivity obiid
unless (actor == LocalActorSharer shr) $
lift $ throwE "Undoing someone else's follow"
lift $ lift $ delete frrid
deleteFollow _ (Right _) = mzero
deleteResolve (Left (_, obiid)) = do
Entity trlid trl <- MaybeT $ lift $ getBy $ UniqueTicketResolveLocalActivity obiid
lift $ lift $ do
let trid = ticketResolveLocalTicket trl
tr <- getJust trid
delete trlid
delete trid
return $ Just $ ticketResolveTicket tr
deleteResolve (Right ractid) = do
Entity trrid trr <- MaybeT $ lift $ getBy $ UniqueTicketResolveRemoteActivity ractid
lift $ lift $ do
let trid = ticketResolveRemoteTicket trr
tr <- getJust trid
delete trrid
delete trid
return $ Just $ ticketResolveTicket tr
pushCommitsC
:: (Entity Person, Sharer)
-> Html

View file

@ -52,6 +52,8 @@ module Vervis.ActivityPub
, insertEmptyOutboxItem
, verifyContentTypeAP
, verifyContentTypeAP_E
, parseActivity
, getActivity
)
where
@ -1208,3 +1210,58 @@ verifyContentTypeAP_E = do
typeAS2 =
"application/ld+json; \
\profile=\"https://www.w3.org/ns/activitystreams\""
parseActivity u@(ObjURI h lu) = do
hl <- hostIsLocal h
if hl
then Left <$> do
route <- fromMaybeE (decodeRouteLocal lu) "Object isn't a valid route"
case route of
SharerOutboxItemR shr obikhid ->
(LocalActorSharer shr,) <$>
decodeKeyHashidE obikhid "No such obikhid"
ProjectOutboxItemR shr prj obikhid -> do
(LocalActorProject shr prj,) <$>
decodeKeyHashidE obikhid "No such obikhid"
RepoOutboxItemR shr rp obikhid -> do
(LocalActorRepo shr rp,) <$>
decodeKeyHashidE obikhid "No such obikhid"
else return $ Right u
getActivity (Left (actor, obiid)) = Just . Left <$> do
obid <- getActorOutbox actor
obi <- do
mobi <- lift $ get obiid
fromMaybeE mobi "No such obiid"
unless (outboxItemOutbox obi == obid) $
throwE "Actor/obiid mismatch"
return (actor, obiid)
where
getActorOutbox (LocalActorSharer shr) = do
sid <- do
msid <- lift $ getKeyBy $ UniqueSharer shr
fromMaybeE msid "No such sharer"
p <- do
mp <- lift $ getValBy $ UniquePersonIdent sid
fromMaybeE mp "No such person"
return $ personOutbox p
getActorOutbox (LocalActorProject shr prj) = do
sid <- do
msid <- lift $ getKeyBy $ UniqueSharer shr
fromMaybeE msid "No such sharer"
j <- do
mj <- lift $ getValBy $ UniqueProject prj sid
fromMaybeE mj "No such project"
return $ projectOutbox j
getActorOutbox (LocalActorRepo shr rp) = do
sid <- do
msid <- lift $ getKeyBy $ UniqueSharer shr
fromMaybeE msid "No such sharer"
r <- do
mr <- lift $ getValBy $ UniqueRepo rp sid
fromMaybeE mr "No such repo"
return $ repoOutbox r
getActivity (Right u@(ObjURI h lu)) = lift $ runMaybeT $ Right <$> do
iid <- MaybeT $ getKeyBy $ UniqueInstance h
roid <- MaybeT $ getKeyBy $ UniqueRemoteObject iid lu
MaybeT $ getKeyBy $ UniqueRemoteActivity roid

View file

@ -23,10 +23,12 @@ module Vervis.Client
, followRepo
, offerTicket
, createTicket
, resolve
, undoFollowSharer
, undoFollowProject
, undoFollowTicket
, undoFollowRepo
, unresolve
)
where
@ -47,7 +49,7 @@ import qualified Data.Text as T
import qualified Data.Text.Lazy as TL
import Network.FedURI
import Web.ActivityPub hiding (Follow, Ticket)
import Web.ActivityPub hiding (Follow, Ticket, Project, Repo)
import Yesod.ActivityPub
import Yesod.FedURI
import Yesod.Hashids
@ -57,13 +59,17 @@ import Yesod.RenderSource
import qualified Web.ActivityPub as AP
import Control.Monad.Trans.Except.Local
import Data.Either.Local
import Database.Persist.Local
import Vervis.ActivityPub
import Vervis.ActivityPub.Recipient
import Vervis.FedURI
import Vervis.Foundation
import Vervis.Model
import Vervis.Model.Ident
import Vervis.Ticket
import Vervis.WorkItem
createThread
:: (MonadSite m, SiteEnv m ~ App)
@ -315,6 +321,37 @@ createTicket shrAuthor (TextHtml title) (TextPandocMarkdown desc) target context
return (summary, audience, create)
resolve
:: (MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
=> ShrIdent
-> FedURI
-> m (Either Text (Maybe TextHtml, Audience URIMode, Resolve URIMode))
resolve shrUser uObject = runExceptT $ do
encodeRouteHome <- getEncodeRouteHome
wiFollowers <- askWorkItemFollowers
object <- parseWorkItem "Resolve object" uObject
WorkItemDetail ident context author <- runWorkerExcept $ getWorkItemDetail "Object" object
let audAuthor =
AudLocal
[LocalActorSharer shrUser]
[LocalPersonCollectionSharerFollowers shrUser]
audTicketContext = contextAudience context
audTicketAuthor = authorAudience author
audTicketFollowers =
case ident of
Left (wi, _ltid) -> AudLocal [] [wiFollowers wi]
Right (ObjURI h _, luFollowers) -> AudRemote h [] [luFollowers]
(_, _, _, audLocal, audRemote) =
collectAudience $
audAuthor :
audTicketAuthor :
audTicketFollowers :
audTicketContext
recips = map encodeRouteHome audLocal ++ audRemote
return (Nothing, Audience recips [] [] [] [] [], Resolve uObject)
undoFollow
:: (MonadUnliftIO m, MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
=> ShrIdent
@ -347,7 +384,7 @@ undoFollow shrAuthor pidAuthor getFsid typ objRoute recipRoute = runExceptT $ do
|]
let undo = Undo
{ undoObject =
encodeRouteLocal $ SharerOutboxItemR shrAuthor obikhidFollow
encodeRouteHome $ SharerOutboxItemR shrAuthor obikhidFollow
}
audience = Audience [encodeRouteHome recipRoute] [] [] [] [] []
return (summary, audience, undo)
@ -442,3 +479,85 @@ undoFollowRepo shrAuthor pidAuthor shrFollowee rpFollowee =
mr <- lift $ getValBy $ UniqueRepo rpFollowee sidFollowee
repoFollowers <$>
fromMaybeE mr "Unfollow target no such local repo"
data ActorEntity
= ActorPerson (Entity Person)
| ActorProject (Entity Project)
| ActorRepo (Entity Repo)
unresolve
:: (MonadUnliftIO m, MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
=> ShrIdent
-> WorkItem
-> m (Either Text (Maybe TextHtml, Audience URIMode, Undo URIMode))
unresolve shrUser wi = runExceptT $ do
encodeRouteHome <- getEncodeRouteHome
wiFollowers <- askWorkItemFollowers
WorkItemDetail ident context author <- runWorkerExcept $ getWorkItemDetail "Object" $ Left wi
ltid <-
case ident of
Left (_, ltid) -> return ltid
Right _ -> error "Local WorkItem expected!"
uResolve <- runSiteDBExcept $ do
mtrid <- lift $ getKeyBy $ UniqueTicketResolve ltid
trid <- fromMaybeE mtrid "Ticket already isn't resolved"
trx <-
lift $
requireEitherAlt
(getValBy $ UniqueTicketResolveLocal trid)
(getValBy $ UniqueTicketResolveRemote trid)
"No TRX"
"Both TRL and TRR"
case trx of
Left trl -> lift $ do
let obiid = ticketResolveLocalActivity trl
obid <- outboxItemOutbox <$> getJust obiid
ent <- getOutboxActorEntity obid
obikhid <- encodeKeyHashid obiid
encodeRouteHome . flip outboxItemRoute obikhid <$>
actorEntityPath ent
Right trr -> lift $ do
roid <-
remoteActivityIdent <$>
getJust (ticketResolveRemoteActivity trr)
ro <- getJust roid
i <- getJust $ remoteObjectInstance ro
return $ ObjURI (instanceHost i) (remoteObjectIdent ro)
let audAuthor =
AudLocal
[LocalActorSharer shrUser]
[LocalPersonCollectionSharerFollowers shrUser]
audTicketContext = contextAudience context
audTicketAuthor = authorAudience author
audTicketFollowers = AudLocal [] [wiFollowers wi]
(_, _, _, audLocal, audRemote) =
collectAudience $
audAuthor :
audTicketAuthor :
audTicketFollowers :
audTicketContext
recips = map encodeRouteHome audLocal ++ audRemote
return (Nothing, Audience recips [] [] [] [] [], Undo uResolve)
where
getOutboxActorEntity obid = do
mp <- getBy $ UniquePersonOutbox obid
mj <- getBy $ UniqueProjectOutbox obid
mr <- getBy $ UniqueRepoOutbox obid
case (mp, mj, mr) of
(Nothing, Nothing, Nothing) -> error "obid not in use"
(Just p, Nothing, Nothing) -> return $ ActorPerson p
(Nothing, Just j, Nothing) -> return $ ActorProject j
(Nothing, Nothing, Just r) -> return $ ActorRepo r
actorEntityPath (ActorPerson (Entity _ p)) =
LocalActorSharer . sharerIdent <$> getJust (personIdent p)
actorEntityPath (ActorProject (Entity _ j)) =
flip LocalActorProject (projectIdent j) . sharerIdent <$>
getJust (projectSharer j)
actorEntityPath (ActorRepo (Entity _ r)) =
flip LocalActorRepo (repoIdent r) . sharerIdent <$>
getJust (repoSharer r)
outboxItemRoute (LocalActorSharer shr) = SharerOutboxItemR shr
outboxItemRoute (LocalActorProject shr prj) = ProjectOutboxItemR shr prj
outboxItemRoute (LocalActorRepo shr rp) = RepoOutboxItemR shr rp

View file

@ -39,6 +39,7 @@ import Data.Aeson
import Data.Bifunctor
import Data.Bitraversable
import Data.ByteString (ByteString)
import Data.Either
import Data.Foldable
import Data.Function
import Data.List (nub, union)
@ -533,73 +534,77 @@ repoFollowF shr rp =
followers (r, Nothing) = repoFollowers r
followers (_, Just lt) = localTicketFollowers lt
undoF
:: Route App
-> AppDB (Entity a)
-> (a -> InboxId)
-> (a -> FollowerSetId)
-> (Key a -> FollowerSetId -> AppDB (Maybe Text))
-> UTCTime
-> RemoteAuthor
-> ActivityBody
-> Maybe (LocalRecipientSet, ByteString)
-> LocalURI
-> Undo URIMode
-> ExceptT Text Handler Text
undoF
recipRoute getRecip recipInbox recipFollowers trySubObjects
now author body mfwd luUndo (Undo luObj) = do
lift $ runDB $ do
Entity idRecip recip <- getRecip
ractid <- insertActivity luUndo
mreason <- deleteRemoteFollow idRecip (recipFollowers recip)
case mreason of
Just reason -> return $ "Not using this Undo: " <> reason
Nothing -> do
inserted <- insertToInbox (recipInbox recip) ractid
encodeRouteLocal <- getEncodeRouteLocal
let me = localUriPath $ encodeRouteLocal recipRoute
return $
if inserted
then "Undo applied and inserted to inbox of " <> me
else "Undo applied and already exists in inbox of " <> me
getFollow (Left _) = return Nothing
getFollow (Right ractid) = getBy $ UniqueRemoteFollowFollow ractid
getResolve (Left (_, obiid)) = fmap Left <$> getBy (UniqueTicketResolveLocalActivity obiid)
getResolve (Right ractid) = fmap Right <$> getBy (UniqueTicketResolveRemoteActivity ractid)
deleteResolve myWorkItem prepareAccept tr = do
let (trid, trxid) =
case tr of
Left (Entity trlid trl) -> (ticketResolveLocalTicket trl, Left trlid)
Right (Entity trrid trr) -> (ticketResolveRemoteTicket trr, Right trrid)
ltid <- ticketResolveTicket <$> getJust trid
wi <- getWorkItem ltid
case myWorkItem wi of
Nothing -> return ("Undo is of a TicketResolve but not my ticket", Nothing, Nothing)
Just wiData -> do
bitraverse delete delete trxid
delete trid
(colls, accept) <- prepareAccept wiData
return ("Ticket unresolved", Just colls, Just accept)
deleteRemoteFollow myWorkItem author fsidRecip (Entity rfid rf)
| remoteFollowActor rf /= remoteAuthorId author =
return "Undo sent by different actor than the one who sent the Follow"
| remoteFollowTarget rf == fsidRecip = do
delete rfid
return "Undo applied to sharer RemoteFollow"
| otherwise = do
r <- tryTicket $ remoteFollowTarget rf
when (isRight r) $ delete rfid
return $ either id id r
where
insertActivity luUndo = do
let iidAuthor = remoteAuthorInstance author
roid <-
either entityKey id <$> insertBy' (RemoteObject iidAuthor luUndo)
let jsonObj = persistJSONFromBL $ actbBL body
ract = RemoteActivity roid jsonObj now
either entityKey id <$> insertBy' ract
deleteRemoteFollow idRecip fsidRecip = do
let iidAuthor = remoteAuthorInstance author
mractidObj <- runMaybeT $ do
roid <- MaybeT $ getKeyBy $ UniqueRemoteObject iidAuthor luObj
MaybeT $ getKeyBy $ UniqueRemoteActivity roid
case mractidObj of
Nothing -> return $ Just "Undo object isn't a known activity"
Just ractidObj -> do
merf <- getBy $ UniqueRemoteFollowFollow ractidObj
case merf of
Nothing -> return $ Just "Undo object doesn't match an active RemoteFollow"
Just (Entity rfid rf)
| remoteFollowActor rf /= remoteAuthorId author ->
return $ Just "Undo sent by different actor than the one who sent the Follow"
| remoteFollowTarget rf == fsidRecip -> do
delete rfid
return Nothing
| otherwise -> do
mr <- trySubObjects idRecip (remoteFollowTarget rf)
when (isNothing mr) $ delete rfid
return mr
insertToInbox ibidRecip ractid = do
ibiid <- insert $ InboxItem False
mibrid <- insertUnique $ InboxItemRemote ibidRecip ractid ibiid
case mibrid of
Nothing -> do
delete ibiid
return False
Just _ -> return True
tryTicket fsid = do
mltid <- getKeyBy $ UniqueLocalTicketFollowers fsid
case mltid of
Nothing -> return $ Left "Undo object is a RemoteFollow, but not for me and not for a ticket"
Just ltid -> do
wi <- getWorkItem ltid
return $
if myWorkItem wi
then Right "Undo applied to RemoteFollow of my ticket"
else Left "Undo is of RemoteFollow of a ticket that isn't mine"
insertAcceptOnUndo actor author luUndo obiid auds = do
encodeRouteLocal <- getEncodeRouteLocal
encodeRouteHome <- getEncodeRouteHome
hLocal <- asksSite siteInstanceHost
obikhid <- encodeKeyHashid obiid
let hAuthor = objUriAuthority $ remoteAuthorURI author
(recipientSet, remoteActors, fwdHosts, audLocal, audRemote) =
collectAudience auds
recips = map encodeRouteHome audLocal ++ audRemote
doc = Doc hLocal Activity
{ activityId =
Just $ encodeRouteLocal $ actorOutboxItem actor obikhid
, activityActor = encodeRouteLocal $ renderLocalActor actor
, activitySummary = Nothing
, activityAudience = Audience recips [] [] [] [] []
, activitySpecific = AcceptActivity Accept
{ acceptObject = ObjURI hAuthor luUndo
, acceptResult = Nothing
}
}
update obiid [OutboxItemActivity =. persistJSONObjectFromDoc doc]
return (doc, recipientSet, remoteActors, fwdHosts)
where
actorOutboxItem (LocalActorSharer shr) = SharerOutboxItemR shr
actorOutboxItem (LocalActorProject shr prj) = ProjectOutboxItemR shr prj
actorOutboxItem (LocalActorRepo shr rp) = RepoOutboxItemR shr rp
sharerUndoF
:: ShrIdent
@ -610,32 +615,88 @@ sharerUndoF
-> LocalURI
-> Undo URIMode
-> ExceptT Text Handler Text
sharerUndoF shr =
undoF
(SharerR shr)
getRecip
personInbox
personFollowers
tryTicket
sharerUndoF shrRecip now author body mfwd luUndo (Undo uObj) = do
object <- parseActivity uObj
mmmhttp <- runDBExcept $ do
p <- lift $ do
sid <- getKeyBy404 $ UniqueSharer shrRecip
getValBy404 $ UniquePersonIdent sid
mractid <- lift $ insertToInbox now author body (personInbox p) luUndo True
for mractid $ \ ractid -> do
mobject' <- getActivity object
lift $ for mobject' $ \ object' -> do
mobject'' <- runMaybeT $
Left <$> MaybeT (getFollow object') <|>
Right <$> MaybeT (getResolve object')
for mobject'' $ \ object'' -> do
(result, mfwdColl, macceptAuds) <-
case object'' of
Left erf -> (,Nothing,Nothing) <$> deleteRemoteFollow (isJust . myWorkItem) author (personFollowers p) erf
Right tr -> deleteResolve myWorkItem prepareAccept tr
mremotesHttpFwd <- for (liftA2 (,) mfwd mfwdColl) $ \ ((localRecips, sig), colls) -> do
let sieve = makeRecipientSet [] colls
remoteRecips <-
insertRemoteActivityToLocalInboxes
False ractid $
localRecipSieve'
sieve False False localRecips
(sig,) <$> deliverRemoteDB_S (actbBL body) ractid (personIdent p) sig remoteRecips
mremotesHttpAccept <- for macceptAuds $ \ acceptAuds -> do
obiidAccept <- insertEmptyOutboxItem (personOutbox p) now
(docAccept, localRecipsAccept, remoteRecipsAccept, fwdHostsAccept) <-
insertAcceptOnUndo (LocalActorSharer shrRecip) author luUndo obiidAccept acceptAuds
knownRemoteRecipsAccept <-
deliverLocal'
False
(LocalActorSharer shrRecip)
(personInbox p)
obiidAccept
localRecipsAccept
(obiidAccept,docAccept,fwdHostsAccept,) <$>
deliverRemoteDB'' fwdHostsAccept obiidAccept remoteRecipsAccept knownRemoteRecipsAccept
return (result, mremotesHttpFwd, mremotesHttpAccept)
case mmmhttp of
Nothing -> return "Activity already in my inbox"
Just mmhttp ->
case mmhttp of
Nothing -> return "Undo object isn't a known activity"
Just mhttp ->
case mhttp of
Nothing -> return "Undo object isn't in use"
Just (msg, mremotesHttpFwd, mremotesHttpAccept) -> do
for_ mremotesHttpFwd $ \ (sig, remotes) ->
forkWorker "sharerUndoF inbox-forwarding" $
deliverRemoteHTTP_S now shrRecip (actbBL body) sig remotes
for_ mremotesHttpAccept $ \ (obiid, doc, fwdHosts, remotes) ->
forkWorker "sharerUndoF Accept HTTP delivery" $
deliverRemoteHttp' fwdHosts obiid doc remotes
let fwdMsg =
case mremotesHttpFwd of
Nothing -> "No inbox-forwarding"
Just _ -> "Did inbox-forwarding"
acceptMsg =
case mremotesHttpAccept of
Nothing -> "Didn't send Accept"
Just _ -> "Sent Accept"
return $ msg <> "; " <> fwdMsg <> "; " <> acceptMsg
where
getRecip = do
sid <- getKeyBy404 $ UniqueSharer shr
getBy404 $ UniquePersonIdent sid
tryTicket pid fsid = do
mltid <- getKeyBy $ UniqueLocalTicketFollowers fsid
case mltid of
Nothing -> return $ Just "Undo object is a RemoteFollow, but isn't under this sharer"
Just ltid -> do
mtal <- getBy $ UniqueTicketAuthorLocal ltid
case mtal of
Just (Entity talid tal)
| ticketAuthorLocalAuthor tal == pid -> do
mtup <- getBy $ UniqueTicketUnderProjectAuthor talid
return $
case mtup of
Nothing -> Nothing
Just _ -> Just "Undo object is a RemoteFollow of a ticket authored by this sharer, but is hosted by the project"
_ -> return $ Just "Undo object is a RemoteFollow of a ticket of another author"
myWorkItem (WorkItemSharerTicket shr talid patch)
| shr == shrRecip = Just (talid, patch)
myWorkItem _ = Nothing
prepareAccept (talid, patch) = do
talkhid <- encodeKeyHashid talid
ra <- getJust $ remoteAuthorId author
let ObjURI hAuthor luAuthor = remoteAuthorURI author
ticketFollowers =
if patch
then LocalPersonCollectionSharerPatchFollowers shrRecip talkhid
else LocalPersonCollectionSharerTicketFollowers shrRecip talkhid
audAuthor =
AudRemote hAuthor [luAuthor] (maybeToList $ remoteActorFollowers ra)
audTicket =
AudLocal [] [ticketFollowers]
return ([ticketFollowers], [audAuthor, audTicket])
projectUndoF
:: ShrIdent
@ -647,35 +708,86 @@ projectUndoF
-> LocalURI
-> Undo URIMode
-> ExceptT Text Handler Text
projectUndoF shr prj =
undoF
(ProjectR shr prj)
getRecip
projectInbox
projectFollowers
tryTicket
projectUndoF shrRecip prjRecip now author body mfwd luUndo (Undo uObj) = do
object <- parseActivity uObj
mmmhttp <- runDBExcept $ do
Entity jid j <- lift $ do
sid <- getKeyBy404 $ UniqueSharer shrRecip
getBy404 $ UniqueProject prjRecip sid
mractid <- lift $ insertToInbox now author body (projectInbox j) luUndo False
for mractid $ \ ractid -> do
mobject' <- getActivity object
lift $ for mobject' $ \ object' -> do
mobject'' <- runMaybeT $
Left <$> MaybeT (getFollow object') <|>
Right <$> MaybeT (getResolve object')
for mobject'' $ \ object'' -> do
(result, mfwdColl, macceptAuds) <-
case object'' of
Left erf -> (,Nothing,Nothing) <$> deleteRemoteFollow (isJust . myWorkItem) author (projectFollowers j) erf
Right tr -> deleteResolve myWorkItem prepareAccept tr
mremotesHttpFwd <- for (liftA2 (,) mfwd mfwdColl) $ \ ((localRecips, sig), colls) -> do
let sieve = makeRecipientSet [] colls
remoteRecips <-
insertRemoteActivityToLocalInboxes
False ractid $
localRecipSieve'
sieve False False localRecips
(sig,) <$> deliverRemoteDB_J (actbBL body) ractid jid sig remoteRecips
mremotesHttpAccept <- for macceptAuds $ \ acceptAuds -> do
obiidAccept <- insertEmptyOutboxItem (projectOutbox j) now
(docAccept, localRecipsAccept, remoteRecipsAccept, fwdHostsAccept) <-
insertAcceptOnUndo (LocalActorProject shrRecip prjRecip) author luUndo obiidAccept acceptAuds
knownRemoteRecipsAccept <-
deliverLocal'
False
(LocalActorProject shrRecip prjRecip)
(projectInbox j)
obiidAccept
localRecipsAccept
(obiidAccept,docAccept,fwdHostsAccept,) <$>
deliverRemoteDB'' fwdHostsAccept obiidAccept remoteRecipsAccept knownRemoteRecipsAccept
return (result, mremotesHttpFwd, mremotesHttpAccept)
case mmmhttp of
Nothing -> return "Activity already in my inbox"
Just mmhttp ->
case mmhttp of
Nothing -> return "Undo object isn't a known activity"
Just mhttp ->
case mhttp of
Nothing -> return "Undo object isn't in use"
Just (msg, mremotesHttpFwd, mremotesHttpAccept) -> do
for_ mremotesHttpFwd $ \ (sig, remotes) ->
forkWorker "projectUndoF inbox-forwarding" $
deliverRemoteHTTP_J now shrRecip prjRecip (actbBL body) sig remotes
for_ mremotesHttpAccept $ \ (obiid, doc, fwdHosts, remotes) ->
forkWorker "projectUndoF Accept HTTP delivery" $
deliverRemoteHttp' fwdHosts obiid doc remotes
let fwdMsg =
case mremotesHttpFwd of
Nothing -> "No inbox-forwarding"
Just _ -> "Did inbox-forwarding"
acceptMsg =
case mremotesHttpAccept of
Nothing -> "Didn't send Accept"
Just _ -> "Sent Accept"
return $ msg <> "; " <> fwdMsg <> "; " <> acceptMsg
where
getRecip = do
sid <- getKeyBy404 $ UniqueSharer shr
getBy404 $ UniqueProject prj sid
tryTicket jid fsid = do
mlt <- getValBy $ UniqueLocalTicketFollowers fsid
case mlt of
Nothing -> return $ Just "Undo object is a RemoteFollow, but isn't under this project"
Just lt -> do
mtpl <- runMaybeT $ do
tclid <- MaybeT $ getKeyBy $ UniqueTicketContextLocal $ localTicketTicket lt
tpl <- MaybeT $ getValBy $ UniqueTicketProjectLocal tclid
return (tclid, tpl)
case mtpl of
Just (tclid, tpl)
| ticketProjectLocalProject tpl == jid -> do
mtup <- getBy $ UniqueTicketUnderProjectProject tclid
return $
case mtup of
Nothing -> Just "Undo object is a RemoteFollow of a ticket under this project, but is hosted by the author"
Just _ -> Nothing
_ -> return $ Just "Undo object is a RemoteFollow of a ticket of another project"
myWorkItem (WorkItemProjectTicket shr prj ltid)
| shr == shrRecip && prj == prjRecip = Just ltid
myWorkItem _ = Nothing
prepareAccept ltid = do
ltkhid <- encodeKeyHashid ltid
ra <- getJust $ remoteAuthorId author
let ObjURI hAuthor luAuthor = remoteAuthorURI author
ticketFollowers =
LocalPersonCollectionProjectTicketFollowers shrRecip prjRecip ltkhid
audAuthor =
AudRemote hAuthor [luAuthor] (maybeToList $ remoteActorFollowers ra)
audTicket =
AudLocal [] [ticketFollowers]
return ([ticketFollowers], [audAuthor, audTicket])
repoUndoF
:: ShrIdent
@ -687,32 +799,83 @@ repoUndoF
-> LocalURI
-> Undo URIMode
-> ExceptT Text Handler Text
repoUndoF shr rp =
undoF
(RepoR shr rp)
getRecip
repoInbox
repoFollowers
tryPatch
repoUndoF shrRecip rpRecip now author body mfwd luUndo (Undo uObj) = do
object <- parseActivity uObj
mmmhttp <- runDBExcept $ do
Entity rid r <- lift $ do
sid <- getKeyBy404 $ UniqueSharer shrRecip
getBy404 $ UniqueRepo rpRecip sid
mractid <- lift $ insertToInbox now author body (repoInbox r) luUndo False
for mractid $ \ ractid -> do
mobject' <- getActivity object
lift $ for mobject' $ \ object' -> do
mobject'' <- runMaybeT $
Left <$> MaybeT (getFollow object') <|>
Right <$> MaybeT (getResolve object')
for mobject'' $ \ object'' -> do
(result, mfwdColl, macceptAuds) <-
case object'' of
Left erf -> (,Nothing,Nothing) <$> deleteRemoteFollow (isJust . myWorkItem) author (repoFollowers r) erf
Right tr -> deleteResolve myWorkItem prepareAccept tr
mremotesHttpFwd <- for (liftA2 (,) mfwd mfwdColl) $ \ ((localRecips, sig), colls) -> do
let sieve = makeRecipientSet [] colls
remoteRecips <-
insertRemoteActivityToLocalInboxes
False ractid $
localRecipSieve'
sieve False False localRecips
(sig,) <$> deliverRemoteDB_R (actbBL body) ractid rid sig remoteRecips
mremotesHttpAccept <- for macceptAuds $ \ acceptAuds -> do
obiidAccept <- insertEmptyOutboxItem (repoOutbox r) now
(docAccept, localRecipsAccept, remoteRecipsAccept, fwdHostsAccept) <-
insertAcceptOnUndo (LocalActorRepo shrRecip rpRecip) author luUndo obiidAccept acceptAuds
knownRemoteRecipsAccept <-
deliverLocal'
False
(LocalActorRepo shrRecip rpRecip)
(repoInbox r)
obiidAccept
localRecipsAccept
(obiidAccept,docAccept,fwdHostsAccept,) <$>
deliverRemoteDB'' fwdHostsAccept obiidAccept remoteRecipsAccept knownRemoteRecipsAccept
return (result, mremotesHttpFwd, mremotesHttpAccept)
case mmmhttp of
Nothing -> return "Activity already in my inbox"
Just mmhttp ->
case mmhttp of
Nothing -> return "Undo object isn't a known activity"
Just mhttp ->
case mhttp of
Nothing -> return "Undo object isn't in use"
Just (msg, mremotesHttpFwd, mremotesHttpAccept) -> do
for_ mremotesHttpFwd $ \ (sig, remotes) ->
forkWorker "repoUndoF inbox-forwarding" $
deliverRemoteHTTP_R now shrRecip rpRecip (actbBL body) sig remotes
for_ mremotesHttpAccept $ \ (obiid, doc, fwdHosts, remotes) ->
forkWorker "repoUndoF Accept HTTP delivery" $
deliverRemoteHttp' fwdHosts obiid doc remotes
let fwdMsg =
case mremotesHttpFwd of
Nothing -> "No inbox-forwarding"
Just _ -> "Did inbox-forwarding"
acceptMsg =
case mremotesHttpAccept of
Nothing -> "Didn't send Accept"
Just _ -> "Sent Accept"
return $ msg <> "; " <> fwdMsg <> "; " <> acceptMsg
where
getRecip = do
sid <- getKeyBy404 $ UniqueSharer shr
getBy404 $ UniqueRepo rp sid
tryPatch rid fsid = do
mlt <- getValBy $ UniqueLocalTicketFollowers fsid
case mlt of
Nothing -> return $ Just "Undo object is a RemoteFollow, but isn't under this repo"
Just lt -> do
mtrl <- runMaybeT $ do
tclid <- MaybeT $ getKeyBy $ UniqueTicketContextLocal $ localTicketTicket lt
trl <- MaybeT $ getValBy $ UniqueTicketRepoLocal tclid
return (tclid, trl)
case mtrl of
Just (tclid, trl)
| ticketRepoLocalRepo trl == rid -> do
mtup <- getBy $ UniqueTicketUnderProjectProject tclid
return $
case mtup of
Nothing -> Just "Undo object is a RemoteFollow of a patch under this repo, but is hosted by the author"
Just _ -> Nothing
_ -> return $ Just "Undo object is a RemoteFollow of a ticket of another project"
myWorkItem (WorkItemRepoPatch shr rp ltid)
| shr == shrRecip && rp == rpRecip = Just ltid
myWorkItem _ = Nothing
prepareAccept ltid = do
ltkhid <- encodeKeyHashid ltid
ra <- getJust $ remoteAuthorId author
let ObjURI hAuthor luAuthor = remoteAuthorURI author
ticketFollowers =
LocalPersonCollectionRepoPatchFollowers shrRecip rpRecip ltkhid
audAuthor =
AudRemote hAuthor [luAuthor] (maybeToList $ remoteActorFollowers ra)
audTicket =
AudLocal [] [ticketFollowers]
return ([ticketFollowers], [audAuthor, audTicket])

View file

@ -256,8 +256,6 @@ insertLocalTicket now author txl summary content source ractidOffer obiidAccept
, ticketDescription = unTextHtml content
, ticketAssignee = Nothing
, ticketStatus = TSNew
, ticketClosed = UTCTime (ModifiedJulianDay 0) 0
, ticketCloser = Nothing
}
ltid <- insert LocalTicket
{ localTicketTicket = tid
@ -804,8 +802,6 @@ insertRemoteTicket mktxl author luTicket published summary content source ractid
, ticketDescription = unTextHtml content
, ticketAssignee = Nothing
, ticketStatus = TSNew
, ticketClosed = UTCTime (ModifiedJulianDay 0) 0
, ticketCloser = Nothing
}
tclid <- insert TicketContextLocal
{ ticketContextLocalTicket = tid

View file

@ -156,8 +156,6 @@ editTicketContentAForm ticket = Ticket
<*> pure (ticketDescription ticket)
<*> pure (ticketAssignee ticket)
<*> pure (ticketStatus ticket)
<*> pure (ticketClosed ticket)
<*> pure (ticketCloser ticket)
tEditField
:: TicketTextParam

View file

@ -34,6 +34,8 @@ module Vervis.Handler.Client
, postNotificationsR
, postProjectTicketsR
, postProjectTicketCloseR
, postProjectTicketOpenR
)
where
@ -90,6 +92,7 @@ import Vervis.Model.Ident
import Vervis.Model.Repo
import Vervis.Path
import Vervis.Settings
import Vervis.Ticket
import qualified Vervis.Client as C
import qualified Vervis.Darcs as D
@ -244,6 +247,12 @@ getUser = do
s <- runDB $ getJust $ personIdent p
return (sharerIdent s, pid)
getUser' :: Handler (Entity Person, Sharer)
getUser' = do
ep@(Entity _ p) <- requireVerifiedAuth
s <- runDB $ getJust $ personIdent p
return (ep, s)
getUserShrIdent :: Handler ShrIdent
getUserShrIdent = fst <$> getUser
@ -305,8 +314,10 @@ postSharerOutboxR shr = do
OfferDep dep ->
offerDepC eperson sharer summary audience dep target
_ -> throwE "Unsupported Offer 'object' type"
ResolveActivity resolve ->
resolveC eperson sharer summary audience resolve
UndoActivity undo ->
undoC shr summary audience undo
undoC eperson sharer summary audience undo
_ -> throwE "Unsupported activity type"
postPublishR :: Handler Html
@ -572,41 +583,45 @@ setUnfollowMessage shr (Right obiid) = do
postSharerUnfollowR :: ShrIdent -> Handler ()
postSharerUnfollowR shrFollowee = do
(shrAuthor, pidAuthor) <- getUser
(ep@(Entity pid _), s) <- getUser'
let shrAuthor = sharerIdent s
eid <- runExceptT $ do
(summary, audience, undo) <-
ExceptT $ undoFollowSharer shrAuthor pidAuthor shrFollowee
undoC shrAuthor (Just summary) audience undo
ExceptT $ undoFollowSharer shrAuthor pid shrFollowee
undoC ep s (Just summary) audience undo
setUnfollowMessage shrAuthor eid
redirect $ SharerR shrFollowee
postProjectUnfollowR :: ShrIdent -> PrjIdent -> Handler ()
postProjectUnfollowR shrFollowee prjFollowee = do
(shrAuthor, pidAuthor) <- getUser
(ep@(Entity pid _), s) <- getUser'
let shrAuthor = sharerIdent s
eid <- runExceptT $ do
(summary, audience, undo) <-
ExceptT $ undoFollowProject shrAuthor pidAuthor shrFollowee prjFollowee
undoC shrAuthor (Just summary) audience undo
ExceptT $ undoFollowProject shrAuthor pid shrFollowee prjFollowee
undoC ep s (Just summary) audience undo
setUnfollowMessage shrAuthor eid
redirect $ ProjectR shrFollowee prjFollowee
postProjectTicketUnfollowR :: ShrIdent -> PrjIdent -> KeyHashid LocalTicket -> Handler ()
postProjectTicketUnfollowR shrFollowee prjFollowee tkhidFollowee = do
(shrAuthor, pidAuthor) <- getUser
(ep@(Entity pid _), s) <- getUser'
let shrAuthor = sharerIdent s
eid <- runExceptT $ do
(summary, audience, undo) <-
ExceptT $ undoFollowTicket shrAuthor pidAuthor shrFollowee prjFollowee tkhidFollowee
undoC shrAuthor (Just summary) audience undo
ExceptT $ undoFollowTicket shrAuthor pid shrFollowee prjFollowee tkhidFollowee
undoC ep s (Just summary) audience undo
setUnfollowMessage shrAuthor eid
redirect $ ProjectTicketR shrFollowee prjFollowee tkhidFollowee
postRepoUnfollowR :: ShrIdent -> RpIdent -> Handler ()
postRepoUnfollowR shrFollowee rpFollowee = do
(shrAuthor, pidAuthor) <- getUser
(ep@(Entity pid _), s) <- getUser'
let shrAuthor = sharerIdent s
eid <- runExceptT $ do
(summary, audience, undo) <-
ExceptT $ undoFollowRepo shrAuthor pidAuthor shrFollowee rpFollowee
undoC shrAuthor (Just summary) audience undo
ExceptT $ undoFollowRepo shrAuthor pid shrFollowee rpFollowee
undoC ep s (Just summary) audience undo
setUnfollowMessage shrAuthor eid
redirect $ RepoR shrFollowee rpFollowee
@ -836,3 +851,32 @@ postProjectTicketsR shr prj = do
Left e -> setMessage $ toHtml $ "Ticket created, but following it failed: " <> e
Right _ -> setMessage "Ticket created."
redirect $ ProjectTicketR shr prj ltkhid
postProjectTicketCloseR
:: ShrIdent -> PrjIdent -> KeyHashid LocalTicket -> Handler Html
postProjectTicketCloseR shr prj ltkhid = do
encodeRouteHome <- getEncodeRouteHome
ep@(Entity _ p) <- requireVerifiedAuth
s <- runDB $ getJust $ personIdent p
let uTicket = encodeRouteHome $ ProjectTicketR shr prj ltkhid
result <- runExceptT $ do
(summary, audience, specific) <- ExceptT $ resolve (sharerIdent s) uTicket
resolveC ep s summary audience specific
case result of
Left e -> setMessage $ toHtml $ "Error: " <> e
Right _obiid -> setMessage "Ticket closed"
redirect $ ProjectTicketR shr prj ltkhid
postProjectTicketOpenR
:: ShrIdent -> PrjIdent -> KeyHashid LocalTicket -> Handler Html
postProjectTicketOpenR shr prj ltkhid = do
ep@(Entity _ p) <- requireVerifiedAuth
ltid <- decodeKeyHashid404 ltkhid
s <- runDB $ getJust $ personIdent p
result <- runExceptT $ do
(summary, audience, specific) <- ExceptT $ unresolve (sharerIdent s) (WorkItemProjectTicket shr prj ltid)
undoC ep s summary audience specific
case result of
Left e -> setMessage $ toHtml $ "Error: " <> e
Right _obiid -> setMessage "Ticket reopened"
redirect $ ProjectTicketR shr prj ltkhid

View file

@ -23,8 +23,6 @@ module Vervis.Handler.Ticket
, postProjectTicketR
, getProjectTicketEditR
, postProjectTicketAcceptR
, postProjectTicketCloseR
, postProjectTicketOpenR
, postProjectTicketClaimR
, postProjectTicketUnclaimR
, getProjectTicketAssignR
@ -299,7 +297,7 @@ getProjectTicketR :: ShrIdent -> PrjIdent -> KeyHashid LocalTicket -> Handler Ty
getProjectTicketR shar proj ltkhid = do
mpid <- maybeAuthId
( wshr, wfl,
author, massignee, mcloser, ticket, lticket, tparams, eparams, cparams) <-
author, massignee, ticket, lticket, tparams, eparams, cparams) <-
runDB $ do
(Entity sid sharer, Entity jid project, Entity tid ticket, Entity _ lticket, _etcl, _etpl, author) <- getProjectTicket404 shar proj ltkhid
(wshr, wid, wfl) <- do
@ -327,24 +325,12 @@ getProjectTicketR shar proj ltkhid = do
person <- get404 apid
sharer <- get404 $ personIdent person
return (sharer, fromMaybe False $ (== apid) <$> mpid)
mcloser <-
case ticketStatus ticket of
TSClosed ->
case ticketCloser ticket of
Just pidCloser -> Just <$> do
person <- getJust pidCloser
getJust $ personIdent person
Nothing -> error "Closer not set for closed ticket"
_ ->
case ticketCloser ticket of
Just _ -> error "Closer set for open ticket"
Nothing -> return Nothing
tparams <- getTicketTextParams tid wid
eparams <- getTicketEnumParams tid wid
cparams <- getTicketClasses tid wid
return
( wshr, wfl
, author', massignee, mcloser, ticket, lticket
, author', massignee, ticket, lticket
, tparams, eparams, cparams
)
encodeHid <- getEncodeKeyHashid
@ -517,50 +503,6 @@ postProjectTicketAcceptR shr prj ltkhid = do
else "Ticket is already accepted."
redirect $ ProjectTicketR shr prj ltkhid
postProjectTicketCloseR
:: ShrIdent -> PrjIdent -> KeyHashid LocalTicket -> Handler Html
postProjectTicketCloseR shr prj ltkhid = do
pid <- requireAuthId
now <- liftIO getCurrentTime
succ <- runDB $ do
(_es, _ej, Entity tid ticket, _elt, _etcl, _etpl, _author) <- getProjectTicket404 shr prj ltkhid
case ticketStatus ticket of
TSClosed -> return False
_ -> do
update tid
[ TicketAssignee =. Nothing
, TicketStatus =. TSClosed
, TicketClosed =. now
, TicketCloser =. Just pid
]
return True
setMessage $
if succ
then "Ticket closed."
else "Ticket is already closed."
redirect $ ProjectTicketR shr prj ltkhid
postProjectTicketOpenR
:: ShrIdent -> PrjIdent -> KeyHashid LocalTicket -> Handler Html
postProjectTicketOpenR shr prj ltkhid = do
pid <- requireAuthId
now <- liftIO getCurrentTime
succ <- runDB $ do
(_es, _ej, Entity tid ticket, _elt, _etcl, _etpl, _author) <- getProjectTicket404 shr prj ltkhid
case ticketStatus ticket of
TSClosed -> do
update tid
[ TicketStatus =. TSTodo
, TicketCloser =. Nothing
]
return True
_ -> return False
setMessage $
if succ
then "Ticket reopened"
else "Ticket is already open."
redirect $ ProjectTicketR shr prj ltkhid
postProjectTicketClaimR
:: ShrIdent -> PrjIdent -> KeyHashid LocalTicket -> Handler Html
postProjectTicketClaimR shr prj ltkhid = do

View file

@ -1748,6 +1748,10 @@ changes hLocal ctx =
obiidAccept <- insert $ OutboxItem276 obidHoster doc closed
trid <- insert $ TicketResolve276 ltid obiidAccept
insert_ $ TicketResolveLocal276 trid obiidResolve
-- 277
, removeField "Ticket" "closed"
-- 278
, removeField "Ticket" "closer"
]
migrateDB

View file

@ -18,6 +18,7 @@ module Vervis.WorkItem
, getWorkItemAuthorDetail
, askWorkItemFollowers
, contextAudience
, authorAudience
, getWorkItemDetail
, WorkItemTarget (..)
)
@ -133,6 +134,9 @@ contextAudience ctx =
, AudRemote hProject [] (catMaybes [luFollowers, luTeam])
]
authorAudience (Left shr) = AudLocal [LocalActorSharer shr] []
authorAudience (Right (ObjURI h lu)) = AudRemote h [lu] []
getWorkItemDetail
:: Text -> Either WorkItem FedURI -> ExceptT Text Worker WorkItemDetail
getWorkItemDetail name v = do

View file

@ -1344,14 +1344,14 @@ encodeResolve :: UriMode u => Resolve u -> Series
encodeResolve (Resolve obj) = "object" .= obj
data Undo u = Undo
{ undoObject :: LocalURI
{ undoObject :: ObjURI u
}
parseUndo :: UriMode u => Authority u -> Object -> Parser (Undo u)
parseUndo a o = Undo <$> withAuthorityO a (o .: "object")
parseUndo a o = Undo <$> o .: "object"
encodeUndo :: UriMode u => Authority u -> Undo u -> Series
encodeUndo a (Undo obj) = "object" .= ObjURI a obj
encodeUndo a (Undo obj) = "object" .= obj
data SpecificActivity u
= AcceptActivity (Accept u)

View file

@ -28,6 +28,7 @@ module Yesod.MonadSite
, runWorkerT
, WorkerFor
, runWorker
, runWorkerExcept
, forkWorker
, asyncWorker
)
@ -198,6 +199,10 @@ type WorkerFor site = WorkerT site IO
runWorker :: (Yesod site, Site site) => WorkerFor site a -> site -> IO a
runWorker = runWorkerT
runWorkerExcept action = do
site <- askSite
ExceptT $ liftIO $ runWorker (runExceptT action) site
forkWorker
:: (MonadSite m, Yesod site, Site site, SiteEnv m ~ site)
=> Text

View file

@ -77,9 +77,7 @@ $if ticketStatus ticket /= TSClosed
^{buttonW POST "Close this ticket" (ProjectTicketCloseR shar proj ltkhid)}
$of TSClosed
Closed on #{showDate $ ticketClosed ticket}
$maybe closer <- mcloser
by ^{sharerLinkW closer}.
Closed on ___ by ___.
^{buttonW POST "Reopen this ticket" (ProjectTicketOpenR shar proj ltkhid)}