Implement C2S unfollowing, using Undo{Follow}

This commit is contained in:
fr33domlover 2019-10-05 14:10:29 +00:00
parent 6a4975a52c
commit bbe6f159d0
17 changed files with 384 additions and 53 deletions

View file

@ -64,6 +64,7 @@
/s/#ShrIdent/outbox/#OutboxItemKeyHashid SharerOutboxItemR GET /s/#ShrIdent/outbox/#OutboxItemKeyHashid SharerOutboxItemR GET
/s/#ShrIdent/followers SharerFollowersR GET /s/#ShrIdent/followers SharerFollowersR GET
/s/#ShrIdent/follow SharerFollowR POST /s/#ShrIdent/follow SharerFollowR POST
/s/#ShrIdent/unfollow SharerUnfollowR POST
/p PeopleR GET /p PeopleR GET
@ -93,6 +94,7 @@
/s/#ShrIdent/r/#RpIdent/followers RepoFollowersR GET /s/#ShrIdent/r/#RpIdent/followers RepoFollowersR GET
/s/#ShrIdent/r/#RpIdent/edit RepoEditR GET /s/#ShrIdent/r/#RpIdent/edit RepoEditR GET
/s/#ShrIdent/r/#RpIdent/follow RepoFollowR POST /s/#ShrIdent/r/#RpIdent/follow RepoFollowR POST
/s/#ShrIdent/r/#RpIdent/unfollow RepoUnfollowR POST
/s/#ShrIdent/r/#RpIdent/s/+Texts RepoSourceR GET /s/#ShrIdent/r/#RpIdent/s/+Texts RepoSourceR GET
/s/#ShrIdent/r/#RpIdent/c RepoHeadChangesR GET /s/#ShrIdent/r/#RpIdent/c RepoHeadChangesR GET
/s/#ShrIdent/r/#RpIdent/b/#Text RepoBranchR GET /s/#ShrIdent/r/#RpIdent/b/#Text RepoBranchR GET
@ -117,6 +119,7 @@
/s/#ShrIdent/p/#PrjIdent/followers ProjectFollowersR GET /s/#ShrIdent/p/#PrjIdent/followers ProjectFollowersR GET
/s/#ShrIdent/p/#PrjIdent/edit ProjectEditR GET /s/#ShrIdent/p/#PrjIdent/edit ProjectEditR GET
/s/#ShrIdent/p/#PrjIdent/follow ProjectFollowR POST /s/#ShrIdent/p/#PrjIdent/follow ProjectFollowR POST
/s/#ShrIdent/p/#PrjIdent/unfollow ProjectUnfollowR POST
/s/#ShrIdent/p/#PrjIdent/d ProjectDevsR GET POST /s/#ShrIdent/p/#PrjIdent/d ProjectDevsR GET POST
/s/#ShrIdent/p/#PrjIdent/d/!new ProjectDevNewR GET /s/#ShrIdent/p/#PrjIdent/d/!new ProjectDevNewR GET
/s/#ShrIdent/p/#PrjIdent/d/#ShrIdent ProjectDevR GET DELETE POST /s/#ShrIdent/p/#PrjIdent/d/#ShrIdent ProjectDevR GET DELETE POST
@ -153,6 +156,7 @@
/s/#ShrIdent/p/#PrjIdent/t/#Int/assign TicketAssignR GET POST /s/#ShrIdent/p/#PrjIdent/t/#Int/assign TicketAssignR GET POST
/s/#ShrIdent/p/#PrjIdent/t/#Int/unassign TicketUnassignR POST /s/#ShrIdent/p/#PrjIdent/t/#Int/unassign TicketUnassignR POST
/s/#ShrIdent/p/#PrjIdent/t/#Int/follow TicketFollowR POST /s/#ShrIdent/p/#PrjIdent/t/#Int/follow TicketFollowR POST
/s/#ShrIdent/p/#PrjIdent/t/#Int/unfollow TicketUnfollowR POST
/s/#ShrIdent/p/#PrjIdent/tcr ClaimRequestsProjectR GET /s/#ShrIdent/p/#PrjIdent/tcr ClaimRequestsProjectR GET
/s/#ShrIdent/p/#PrjIdent/t/#Int/cr ClaimRequestsTicketR GET POST /s/#ShrIdent/p/#PrjIdent/t/#Int/cr ClaimRequestsTicketR GET POST
/s/#ShrIdent/p/#PrjIdent/t/#Int/cr/new ClaimRequestNewR GET /s/#ShrIdent/p/#PrjIdent/t/#Int/cr/new ClaimRequestNewR GET

View file

@ -17,6 +17,7 @@ module Vervis.API
( createNoteC ( createNoteC
, followC , followC
, offerTicketC , offerTicketC
, undoC
, pushCommitsC , pushCommitsC
, getFollowersCollection , getFollowersCollection
) )
@ -99,8 +100,8 @@ import Database.Persist.Local
import Yesod.Persist.Local import Yesod.Persist.Local
import Vervis.ActivityPub import Vervis.ActivityPub
import Vervis.ActivityPub.Recipient
import Vervis.ActorKey import Vervis.ActorKey
import Vervis.API.Recipient
import Vervis.FedURI import Vervis.FedURI
import Vervis.Foundation import Vervis.Foundation
import Vervis.Model import Vervis.Model
@ -883,6 +884,85 @@ offerTicketC shrUser summary audience offer@(Offer ticket uTarget) = runExceptT
insert_ $ InboxItemLocal ibid obiid ibiid insert_ $ InboxItemLocal ibid obiid ibiid
return remotes return remotes
undoC
:: ShrIdent
-> TextHtml
-> Audience URIMode
-> Undo URIMode
-> Handler (Either Text OutboxItemId)
undoC shrUser summary audience undo@(Undo luObject) = runExceptT $ do
(localRecips, remoteRecips) <- do
mrecips <- parseAudience audience
fromMaybeE mrecips "Follow 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"
let dont = Authority "dont-do.any-forwarding" Nothing
(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
let ibidAuthor = personInbox personAuthor
fsidAuthor = personFollowers personAuthor
knownRemotes <- deliverLocal shrUser ibidAuthor fsidAuthor obiidUndo localRecips
remotesHttp <- deliverRemoteDB' dont obiidUndo remoteRecips knownRemotes
return (obiidUndo, doc, remotesHttp)
lift $ forkWorker "undoC: Outbox POST handler: async HTTP delivery" $ deliverRemoteHttp dont obiidUndo doc remotesHttp
return obiidUndo
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 = do
hLocal <- asksSite siteInstanceHost
encodeRouteLocal <- getEncodeRouteLocal
let activity mluAct = Doc hLocal Activity
{ activityId = mluAct
, activityActor = encodeRouteLocal $ SharerR shrUser
, activitySummary = Just summary
, activityAudience = audience
, activitySpecific = UndoActivity undo
}
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)
pushCommitsC pushCommitsC
:: (Entity Person, Sharer) :: (Entity Person, Sharer)
-> Html -> Html

View file

@ -20,7 +20,6 @@ module Vervis.ActivityPub
, parseParent , parseParent
, runDBExcept , runDBExcept
, getLocalParentMessageId , getLocalParentMessageId
, concatRecipients
, getPersonOrGroupId , getPersonOrGroupId
, getTicketTeam , getTicketTeam
, getProjectTeam , getProjectTeam
@ -41,6 +40,7 @@ module Vervis.ActivityPub
, deliverRemoteDB' , deliverRemoteDB'
, deliverRemoteHttp , deliverRemoteHttp
, serveCommit , serveCommit
, deliverLocal
) )
where where
@ -80,6 +80,7 @@ import Yesod.Persist.Core
import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Lazy as BL
import qualified Data.CaseInsensitive as CI import qualified Data.CaseInsensitive as CI
import qualified Data.List.NonEmpty as NE import qualified Data.List.NonEmpty as NE
import qualified Data.List as L
import qualified Data.List.Ordered as LO import qualified Data.List.Ordered as LO
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.Text.Lazy as TL import qualified Data.Text.Lazy as TL
@ -104,6 +105,7 @@ import Data.List.NonEmpty.Local
import Data.Tuple.Local import Data.Tuple.Local
import Database.Persist.Local import Database.Persist.Local
import Vervis.ActivityPub.Recipient
import Vervis.FedURI import Vervis.FedURI
import Vervis.Foundation import Vervis.Foundation
import Vervis.Model import Vervis.Model
@ -190,9 +192,6 @@ getLocalParentMessageId did shr lmid = do
throwE "Local parent belongs to a different discussion" throwE "Local parent belongs to a different discussion"
return mid return mid
concatRecipients :: Audience u -> [ObjURI u]
concatRecipients (Audience to bto cc bcc gen _) = concat [to, bto, cc, bcc, gen]
getPersonOrGroupId :: SharerId -> AppDB (Either PersonId GroupId) getPersonOrGroupId :: SharerId -> AppDB (Either PersonId GroupId)
getPersonOrGroupId sid = do getPersonOrGroupId sid = do
mpid <- getKeyBy $ UniquePersonIdent sid mpid <- getKeyBy $ UniquePersonIdent sid
@ -693,3 +692,74 @@ serveCommit shr rp ref patch parents = do
} }
makeAuthor encodeRouteHome (Just sharer) _ = makeAuthor encodeRouteHome (Just sharer) _ =
Right $ encodeRouteHome $ SharerR $ sharerIdent sharer Right $ encodeRouteHome $ SharerR $ sharerIdent sharer
-- | Given a list of local recipients, which may include actors and
-- collections,
--
-- * Insert activity to inboxes of actors
-- * If the author's follower collection is listed, insert activity to the
-- local members and return the remote members
-- * Ignore other collections
deliverLocal
:: ShrIdent
-> InboxId
-> FollowerSetId
-> Key OutboxItem
-> [(ShrIdent, LocalSharerRelatedSet)]
-> AppDB
[ ( (InstanceId, Host)
, NonEmpty (RemoteActorId, LocalURI, LocalURI, Maybe UTCTime)
)
]
deliverLocal shrAuthor ibidAuthor fsidAuthor obiid recips = do
(pidsFollowers, remotesFollowers) <-
if authorFollowers shrAuthor recips
then getFollowers fsidAuthor
else return ([], [])
ibidsFollowers <-
map (personInbox . entityVal) <$>
selectList [PersonId <-. pidsFollowers] [Asc PersonInbox]
ibidsSharer <- L.delete ibidAuthor <$> getSharerInboxes recips
ibidsOther <- concat <$> traverse getOtherInboxes recips
let ibids = LO.union ibidsFollowers ibidsSharer ++ ibidsOther
ibiids <- insertMany $ replicate (length ibids) $ InboxItem True
insertMany_ $
map (\ (ibid, ibiid) -> InboxItemLocal ibid obiid ibiid)
(zip ibids ibiids)
return remotesFollowers
where
getSharerInboxes sharers = do
let shrs =
[shr | (shr, s) <- sharers
, localRecipSharer $ localRecipSharerDirect s
]
sids <- selectKeysList [SharerIdent <-. shrs] []
map (personInbox . entityVal) <$> selectList [PersonIdent <-. sids] [Asc PersonInbox]
getOtherInboxes (shr, LocalSharerRelatedSet _ projects repos) = do
msid <- getKeyBy $ UniqueSharer shr
case msid of
Nothing -> return []
Just sid ->
(++)
<$> getProjectInboxes sid projects
<*> getRepoInboxes sid repos
where
getProjectInboxes sid projects =
let prjs =
[prj | (prj, j) <- projects
, localRecipProject $ localRecipProjectDirect j
]
in map (projectInbox . entityVal) <$>
selectList [ProjectSharer ==. sid, ProjectIdent <-. prjs] []
getRepoInboxes sid repos =
let rps =
[rp | (rp, r) <- repos
, localRecipRepo $ localRecipRepoDirect r
]
in map (repoInbox . entityVal) <$>
selectList [RepoSharer ==. sid, RepoIdent <-. rps] []
authorFollowers shr lrset =
case lookup shr lrset of
Just s
| localRecipSharerFollowers $ localRecipSharerDirect s -> True
_ -> False

View file

@ -13,14 +13,17 @@
- <http://creativecommons.org/publicdomain/zero/1.0/>. - <http://creativecommons.org/publicdomain/zero/1.0/>.
-} -}
module Vervis.API.Recipient module Vervis.ActivityPub.Recipient
( LocalActor (..) ( LocalActor (..)
, LocalTicketDirectSet (..) , LocalTicketDirectSet (..)
, LocalProjectDirectSet (..) , LocalProjectDirectSet (..)
, LocalProjectRelatedSet (..) , LocalProjectRelatedSet (..)
, LocalRepoDirectSet (..)
, LocalRepoRelatedSet (..)
, LocalSharerDirectSet (..) , LocalSharerDirectSet (..)
, LocalSharerRelatedSet (..) , LocalSharerRelatedSet (..)
, LocalRecipientSet , LocalRecipientSet
, concatRecipients
, parseAudience , parseAudience
, actorRecips , actorRecips
) )
@ -49,11 +52,14 @@ import Yesod.MonadSite
import Data.List.NonEmpty.Local import Data.List.NonEmpty.Local
import Vervis.ActivityPub
import Vervis.FedURI import Vervis.FedURI
import Vervis.Foundation import Vervis.Foundation
import Vervis.Model.Ident import Vervis.Model.Ident
concatRecipients :: Audience u -> [ObjURI u]
concatRecipients (Audience to bto cc bcc gen _) =
concat [to, bto, cc, bcc, gen]
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- Actor and collection-of-persons types -- Actor and collection-of-persons types
-- --

View file

@ -22,11 +22,17 @@ module Vervis.Client
, followTicket , followTicket
, followRepo , followRepo
, offerTicket , offerTicket
, undoFollowSharer
, undoFollowProject
, undoFollowTicket
, undoFollowRepo
) )
where where
import Control.Monad.Trans.Except import Control.Monad.Trans.Except
import Control.Monad.Trans.Reader
import Database.Persist import Database.Persist
import Database.Persist.Sql
import Data.Text (Text) import Data.Text (Text)
import Text.Blaze.Html (preEscapedToHtml) import Text.Blaze.Html (preEscapedToHtml)
import Text.Blaze.Html.Renderer.Text import Text.Blaze.Html.Renderer.Text
@ -40,6 +46,7 @@ import qualified Data.Text.Lazy as TL
import Network.FedURI import Network.FedURI
import Web.ActivityPub hiding (Follow) import Web.ActivityPub hiding (Follow)
import Yesod.ActivityPub
import Yesod.FedURI import Yesod.FedURI
import Yesod.Hashids import Yesod.Hashids
import Yesod.MonadSite import Yesod.MonadSite
@ -47,8 +54,10 @@ import Yesod.RenderSource
import qualified Web.ActivityPub as AP import qualified Web.ActivityPub as AP
import Control.Monad.Trans.Except.Local
import Database.Persist.Local import Database.Persist.Local
import Vervis.ActivityPub
import Vervis.FedURI import Vervis.FedURI
import Vervis.Foundation import Vervis.Foundation
import Vervis.Model import Vervis.Model
@ -242,3 +251,120 @@ offerTicket shrAuthor (TextHtml title) (TextPandocMarkdown desc) shr prj = runEx
, audienceNonActors = map encodeRouteHome recipsC , audienceNonActors = map encodeRouteHome recipsC
} }
return (summary, audience, offer) return (summary, audience, offer)
undoFollow
:: (MonadUnliftIO m, MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
=> ShrIdent
-> PersonId
-> ExceptT Text (ReaderT SqlBackend m) FollowerSetId
-> Text
-> Route App
-> Route App
-> m (Either Text (TextHtml, Audience URIMode, Undo URIMode))
undoFollow shrAuthor pidAuthor getFsid typ objRoute recipRoute = runExceptT $ do
encodeRouteLocal <- getEncodeRouteLocal
encodeRouteHome <- getEncodeRouteHome
obiidFollow <- runDBExcept $ do
fsid <- getFsid
mf <- lift $ getValBy $ UniqueFollow pidAuthor fsid
followFollow <$> fromMaybeE mf ("Not following this " <> typ)
obikhidFollow <- encodeKeyHashid obiidFollow
summary <- do
hLocal <- asksSite siteInstanceHost
TextHtml . TL.toStrict . renderHtml <$>
withUrlRenderer
[hamlet|
<p>
<a href=@{SharerR shrAuthor}>
#{shr2text shrAuthor}
\ unfollowed #
<a href=@{objRoute}>
#{renderAuthority hLocal}#{localUriPath $ encodeRouteLocal objRoute}
\.
|]
let undo = Undo
{ undoObject =
encodeRouteLocal $ SharerOutboxItemR shrAuthor obikhidFollow
}
audience = Audience [encodeRouteHome recipRoute] [] [] [] [] []
return (summary, audience, undo)
undoFollowSharer
:: (MonadUnliftIO m, MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
=> ShrIdent
-> PersonId
-> ShrIdent
-> m (Either Text (TextHtml, Audience URIMode, Undo URIMode))
undoFollowSharer shrAuthor pidAuthor shrFollowee =
undoFollow shrAuthor pidAuthor getFsid "sharer" objRoute objRoute
where
objRoute = SharerR shrFollowee
getFsid = do
sidFollowee <- do
msid <- lift $ getKeyBy $ UniqueSharer shrFollowee
fromMaybeE msid "No such local sharer"
mp <- lift $ getValBy $ UniquePersonIdent sidFollowee
personFollowers <$>
fromMaybeE mp "Unfollow target local sharer isn't a person"
undoFollowProject
:: (MonadUnliftIO m, MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
=> ShrIdent
-> PersonId
-> ShrIdent
-> PrjIdent
-> m (Either Text (TextHtml, Audience URIMode, Undo URIMode))
undoFollowProject shrAuthor pidAuthor shrFollowee prjFollowee =
undoFollow shrAuthor pidAuthor getFsid "project" objRoute objRoute
where
objRoute = ProjectR shrFollowee prjFollowee
getFsid = do
sidFollowee <- do
msid <- lift $ getKeyBy $ UniqueSharer shrFollowee
fromMaybeE msid "No such local sharer"
mj <- lift $ getValBy $ UniqueProject prjFollowee sidFollowee
projectFollowers <$>
fromMaybeE mj "Unfollow target no such local project"
undoFollowTicket
:: (MonadUnliftIO m, MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
=> ShrIdent
-> PersonId
-> ShrIdent
-> PrjIdent
-> Int
-> m (Either Text (TextHtml, Audience URIMode, Undo URIMode))
undoFollowTicket shrAuthor pidAuthor shrFollowee prjFollowee numFollowee =
undoFollow shrAuthor pidAuthor getFsid "project" objRoute recipRoute
where
objRoute = TicketR shrFollowee prjFollowee numFollowee
recipRoute = ProjectR shrFollowee prjFollowee
getFsid = do
sid <- do
msid <- lift $ getKeyBy $ UniqueSharer shrFollowee
fromMaybeE msid "No such local sharer"
jid <- do
mjid <- lift $ getKeyBy $ UniqueProject prjFollowee sid
fromMaybeE mjid "No such local project"
mt <- lift $ getValBy $ UniqueTicket jid numFollowee
ticketFollowers <$>
fromMaybeE mt "Unfollow target no such local ticket"
undoFollowRepo
:: (MonadUnliftIO m, MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
=> ShrIdent
-> PersonId
-> ShrIdent
-> RpIdent
-> m (Either Text (TextHtml, Audience URIMode, Undo URIMode))
undoFollowRepo shrAuthor pidAuthor shrFollowee rpFollowee =
undoFollow shrAuthor pidAuthor getFsid "repo" objRoute objRoute
where
objRoute = RepoR shrFollowee rpFollowee
getFsid = do
sidFollowee <- do
msid <- lift $ getKeyBy $ UniqueSharer shrFollowee
fromMaybeE msid "No such local sharer"
mr <- lift $ getValBy $ UniqueRepo rpFollowee sidFollowee
repoFollowers <$>
fromMaybeE mr "Unfollow target no such local repo"

View file

@ -19,85 +19,54 @@ module Vervis.Federation.Discussion
) )
where where
--import Control.Applicative
--import Control.Concurrent.MVar
--import Control.Concurrent.STM.TVar
import Control.Exception hiding (Handler, try) import Control.Exception hiding (Handler, try)
import Control.Monad import Control.Monad
import Control.Monad.Logger.CallStack import Control.Monad.Logger.CallStack
import Control.Monad.Trans.Class import Control.Monad.Trans.Class
import Control.Monad.Trans.Except import Control.Monad.Trans.Except
import Control.Monad.Trans.Maybe import Control.Monad.Trans.Maybe
--import Control.Monad.Trans.Reader
--import Crypto.Hash
--import Data.Aeson
import Data.Bifunctor import Data.Bifunctor
import Data.ByteString (ByteString) import Data.ByteString (ByteString)
--import Data.Either
import Data.Foldable import Data.Foldable
import Data.Function import Data.Function
import Data.List (sort, deleteBy, nub, union, unionBy, partition) import Data.List (sort, deleteBy, nub, union, unionBy, partition)
import Data.List.NonEmpty (NonEmpty (..), nonEmpty) import Data.List.NonEmpty (NonEmpty (..), nonEmpty)
import Data.Maybe import Data.Maybe
--import Data.Semigroup
import Data.Text (Text) import Data.Text (Text)
import Data.Text.Encoding import Data.Text.Encoding
import Data.Time.Clock import Data.Time.Clock
--import Data.Time.Units
import Data.Traversable import Data.Traversable
--import Data.Tuple
import Database.Persist import Database.Persist
--import Database.Persist.Sql hiding (deleteBy)
--import Network.HTTP.Client
--import Network.HTTP.Types.Header
--import Network.HTTP.Types.URI
--import Network.TLS hiding (SHA256)
--import UnliftIO.Exception (try)
import Yesod.Core hiding (logError, logWarn, logInfo, logDebug) import Yesod.Core hiding (logError, logWarn, logInfo, logDebug)
import Yesod.Persist.Core import Yesod.Persist.Core
import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Lazy as BL
import qualified Data.CaseInsensitive as CI import qualified Data.CaseInsensitive as CI
--import qualified Data.List as L
import qualified Data.List.NonEmpty as NE import qualified Data.List.NonEmpty as NE
--import qualified Data.List.Ordered as LO
import qualified Data.Text as T import qualified Data.Text as T
import qualified Database.Esqueleto as E import qualified Database.Esqueleto as E
--import qualified Network.Wai as W
--import Data.Time.Interval
--import Network.HTTP.Signature hiding (requestHeaders)
import Yesod.HttpSignature import Yesod.HttpSignature
--import Crypto.PublicVerifKey
import Database.Persist.JSON import Database.Persist.JSON
import Network.FedURI import Network.FedURI
import Network.HTTP.Digest import Network.HTTP.Digest
import Web.ActivityPub import Web.ActivityPub
import Yesod.ActivityPub import Yesod.ActivityPub
--import Yesod.Auth.Unverified
import Yesod.FedURI import Yesod.FedURI
--import Yesod.Hashids
--import Yesod.MonadSite
import Control.Monad.Trans.Except.Local import Control.Monad.Trans.Except.Local
--import Data.Aeson.Local
--import Data.Either.Local
--import Data.List.Local
--import Data.List.NonEmpty.Local
--import Data.Maybe.Local
import Data.Tuple.Local import Data.Tuple.Local
import Database.Persist.Local import Database.Persist.Local
import Yesod.Persist.Local import Yesod.Persist.Local
import Vervis.ActivityPub import Vervis.ActivityPub
--import Vervis.ActorKey import Vervis.ActivityPub.Recipient
import Vervis.FedURI import Vervis.FedURI
import Vervis.Federation.Auth import Vervis.Federation.Auth
import Vervis.Foundation import Vervis.Foundation
import Vervis.Model import Vervis.Model
import Vervis.Model.Ident import Vervis.Model.Ident
--import Vervis.RemoteActorStore
import Vervis.Settings import Vervis.Settings
sharerCreateNoteF sharerCreateNoteF

View file

@ -63,6 +63,7 @@ import Database.Persist.Local
import Yesod.Persist.Local import Yesod.Persist.Local
import Vervis.ActivityPub import Vervis.ActivityPub
import Vervis.ActivityPub.Recipient
import Vervis.FedURI import Vervis.FedURI
import Vervis.Federation.Auth import Vervis.Federation.Auth
import Vervis.Foundation import Vervis.Foundation

View file

@ -300,6 +300,7 @@ instance Yesod App where
(NotificationsR shr , _ ) -> person shr (NotificationsR shr , _ ) -> person shr
(SharerOutboxR shr , True) -> person shr (SharerOutboxR shr , True) -> person shr
(SharerFollowR shr , True) -> personAny (SharerFollowR shr , True) -> personAny
(SharerUnfollowR shr , True) -> personAny
(GroupsR , True) -> personAny (GroupsR , True) -> personAny
(GroupNewR , _ ) -> personAny (GroupNewR , _ ) -> personAny
@ -324,6 +325,7 @@ instance Yesod App where
(RepoR shar _ , True) -> person shar (RepoR shar _ , True) -> person shar
(RepoEditR shr _rp , _ ) -> person shr (RepoEditR shr _rp , _ ) -> person shr
(RepoFollowR _shr _rp , True) -> personAny (RepoFollowR _shr _rp , True) -> personAny
(RepoUnfollowR _shr _rp , True) -> personAny
(RepoDevsR shr _rp , _ ) -> person shr (RepoDevsR shr _rp , _ ) -> person shr
(RepoDevNewR shr _rp , _ ) -> person shr (RepoDevNewR shr _rp , _ ) -> person shr
(RepoDevR shr _rp _dev , _ ) -> person shr (RepoDevR shr _rp _dev , _ ) -> person shr
@ -333,6 +335,7 @@ instance Yesod App where
(ProjectR shr _prj , True) -> person shr (ProjectR shr _prj , True) -> person shr
(ProjectEditR shr _prj , _ ) -> person shr (ProjectEditR shr _prj , _ ) -> person shr
(ProjectFollowR _shr _prj , _ ) -> personAny (ProjectFollowR _shr _prj , _ ) -> personAny
(ProjectUnfollowR _shr _prj , _ ) -> personAny
(ProjectDevsR shr _prj , _ ) -> person shr (ProjectDevsR shr _prj , _ ) -> person shr
(ProjectDevNewR shr _prj , _ ) -> person shr (ProjectDevNewR shr _prj , _ ) -> person shr
(ProjectDevR shr _prj _dev , _ ) -> person shr (ProjectDevR shr _prj _dev , _ ) -> person shr
@ -366,6 +369,7 @@ instance Yesod App where
(TicketAssignR s j _ , _ ) -> projOp ProjOpAssignTicket s j (TicketAssignR s j _ , _ ) -> projOp ProjOpAssignTicket s j
(TicketUnassignR s j _ , _ ) -> projOp ProjOpUnassignTicket s j (TicketUnassignR s j _ , _ ) -> projOp ProjOpUnassignTicket s j
(TicketFollowR _ _ _ , True) -> personAny (TicketFollowR _ _ _ , True) -> personAny
(TicketUnfollowR _ _ _ , True) -> personAny
(ClaimRequestsTicketR s j _, True) -> projOp ProjOpRequestTicket s j (ClaimRequestsTicketR s j _, True) -> projOp ProjOpRequestTicket s j
(ClaimRequestNewR s j _ , _ ) -> projOp ProjOpRequestTicket s j (ClaimRequestNewR s j _ , _ ) -> projOp ProjOpRequestTicket s j
(TicketDiscussionR _ _ _ , True) -> personAny (TicketDiscussionR _ _ _ , True) -> personAny

View file

@ -16,12 +16,20 @@
module Vervis.Handler.Client module Vervis.Handler.Client
( getPublishR ( getPublishR
, postSharerOutboxR , postSharerOutboxR
, postSharerFollowR , postSharerFollowR
, postProjectFollowR , postProjectFollowR
, postTicketFollowR , postTicketFollowR
, postRepoFollowR , postRepoFollowR
, postSharerUnfollowR
, postProjectUnfollowR
, postTicketUnfollowR
, postRepoUnfollowR
, getNotificationsR , getNotificationsR
, postNotificationsR , postNotificationsR
, postTicketsR , postTicketsR
) )
where where
@ -199,11 +207,14 @@ activityWidget shr widget1 enctype1 widget2 enctype2 widget3 enctype3 =
<input type=submit> <input type=submit>
|] |]
getUserShrIdent :: Handler ShrIdent getUser :: Handler (ShrIdent, PersonId)
getUserShrIdent = do getUser = do
Entity _ p <- requireVerifiedAuth Entity pid p <- requireVerifiedAuth
s <- runDB $ getJust $ personIdent p s <- runDB $ getJust $ personIdent p
return $ sharerIdent s return (sharerIdent s, pid)
getUserShrIdent :: Handler ShrIdent
getUserShrIdent = fst <$> getUser
getPublishR :: Handler Html getPublishR :: Handler Html
getPublishR = do getPublishR = do
@ -389,6 +400,57 @@ postRepoFollowR shrObject rpObject = do
setFollowMessage shrAuthor eid setFollowMessage shrAuthor eid
redirect $ RepoR shrObject rpObject redirect $ RepoR shrObject rpObject
setUnfollowMessage :: ShrIdent -> Either Text OutboxItemId -> Handler ()
setUnfollowMessage _ (Left err) = setMessage $ toHtml err
setUnfollowMessage shr (Right obiid) = do
obikhid <- encodeKeyHashid obiid
setMessage =<<
withUrlRenderer
[hamlet|
<a href=@{SharerOutboxItemR shr obikhid}>
Unfollow request published!
|]
postSharerUnfollowR :: ShrIdent -> Handler ()
postSharerUnfollowR shrFollowee = do
(shrAuthor, pidAuthor) <- getUser
eid <- runExceptT $ do
(summary, audience, undo) <-
ExceptT $ undoFollowSharer shrAuthor pidAuthor shrFollowee
ExceptT $ undoC shrAuthor summary audience undo
setUnfollowMessage shrAuthor eid
redirect $ SharerR shrFollowee
postProjectUnfollowR :: ShrIdent -> PrjIdent -> Handler ()
postProjectUnfollowR shrFollowee prjFollowee = do
(shrAuthor, pidAuthor) <- getUser
eid <- runExceptT $ do
(summary, audience, undo) <-
ExceptT $ undoFollowProject shrAuthor pidAuthor shrFollowee prjFollowee
ExceptT $ undoC shrAuthor summary audience undo
setUnfollowMessage shrAuthor eid
redirect $ ProjectR shrFollowee prjFollowee
postTicketUnfollowR :: ShrIdent -> PrjIdent -> Int -> Handler ()
postTicketUnfollowR shrFollowee prjFollowee numFollowee = do
(shrAuthor, pidAuthor) <- getUser
eid <- runExceptT $ do
(summary, audience, undo) <-
ExceptT $ undoFollowTicket shrAuthor pidAuthor shrFollowee prjFollowee numFollowee
ExceptT $ undoC shrAuthor summary audience undo
setUnfollowMessage shrAuthor eid
redirect $ TicketR shrFollowee prjFollowee numFollowee
postRepoUnfollowR :: ShrIdent -> RpIdent -> Handler ()
postRepoUnfollowR shrFollowee rpFollowee = do
(shrAuthor, pidAuthor) <- getUser
eid <- runExceptT $ do
(summary, audience, undo) <-
ExceptT $ undoFollowRepo shrAuthor pidAuthor shrFollowee rpFollowee
ExceptT $ undoC shrAuthor summary audience undo
setUnfollowMessage shrAuthor eid
redirect $ RepoR shrFollowee rpFollowee
notificationForm :: Maybe (Maybe (InboxItemId, Bool)) -> Form (Maybe (InboxItemId, Bool)) notificationForm :: Maybe (Maybe (InboxItemId, Bool)) -> Form (Maybe (InboxItemId, Bool))
notificationForm defs = renderDivs $ mk notificationForm defs = renderDivs $ mk
<$> aopt hiddenField (name "Inbox Item ID#") (fmap fst <$> defs) <$> aopt hiddenField (name "Inbox Item ID#") (fmap fst <$> defs)

View file

@ -148,4 +148,7 @@ getPerson shr sharer person = do
provideHtmlAndAP personAP $(widgetFile "person") provideHtmlAndAP personAP $(widgetFile "person")
where where
followButton = followButton =
followW (SharerFollowR shr) (return $ personFollowers person) followW
(SharerFollowR shr)
(SharerUnfollowR shr)
(return $ personFollowers person)

View file

@ -163,6 +163,7 @@ getProjectR shar proj = do
followButton = followButton =
followW followW
(ProjectFollowR shar proj) (ProjectFollowR shar proj)
(ProjectUnfollowR shar proj)
(return $ projectFollowers project) (return $ projectFollowers project)
provideHtmlAndAP projectAP $(widgetFile "project/one") provideHtmlAndAP projectAP $(widgetFile "project/one")

View file

@ -98,7 +98,10 @@ getDarcsRepoSource repository user repo dir = do
$(widgetFile "repo/source-darcs") $(widgetFile "repo/source-darcs")
where where
followButton = followButton =
followW (RepoFollowR user repo) (return $ repoFollowers repository) followW
(RepoFollowR user repo)
(RepoUnfollowR user repo)
(return $ repoFollowers repository)
getDarcsRepoHeadChanges :: ShrIdent -> RpIdent -> Handler TypedContent getDarcsRepoHeadChanges :: ShrIdent -> RpIdent -> Handler TypedContent
getDarcsRepoHeadChanges shar repo = do getDarcsRepoHeadChanges shar repo = do

View file

@ -113,7 +113,10 @@ getGitRepoSource repository user repo ref dir = do
$(widgetFile "repo/source-git") $(widgetFile "repo/source-git")
where where
followButton = followButton =
followW (RepoFollowR user repo) (return $ repoFollowers repository) followW
(RepoFollowR user repo)
(RepoUnfollowR user repo)
(return $ repoFollowers repository)
getGitRepoHeadChanges :: Repo -> ShrIdent -> RpIdent -> Handler TypedContent getGitRepoHeadChanges :: Repo -> ShrIdent -> RpIdent -> Handler TypedContent
getGitRepoHeadChanges repository shar repo = getGitRepoHeadChanges repository shar repo =

View file

@ -295,6 +295,7 @@ getTicketR shar proj num = do
let followButton = let followButton =
followW followW
(TicketFollowR shar proj num) (TicketFollowR shar proj num)
(TicketUnfollowR shar proj num)
(return $ ticketFollowers ticket) (return $ ticketFollowers ticket)
in $(widgetFile "ticket/one") in $(widgetFile "ticket/one")

View file

@ -59,8 +59,8 @@ sharerLinkFedW (Right (inztance, actor)) =
where where
uActor = ObjURI (instanceHost inztance) (remoteActorIdent actor) uActor = ObjURI (instanceHost inztance) (remoteActorIdent actor)
followW :: Route App -> AppDB FollowerSetId -> Widget followW :: Route App -> Route App -> AppDB FollowerSetId -> Widget
followW followRoute getFsid = do followW followRoute unfollowRoute getFsid = do
mpid <- maybeVerifiedAuthId mpid <- maybeVerifiedAuthId
for_ mpid $ \ pid -> do for_ mpid $ \ pid -> do
mfollow <- handlerToWidget $ runDB $ do mfollow <- handlerToWidget $ runDB $ do
@ -68,7 +68,4 @@ followW followRoute getFsid = do
getValBy $ UniqueFollow pid fsid getValBy $ UniqueFollow pid fsid
case mfollow of case mfollow of
Nothing -> buttonW POST "Follow" followRoute Nothing -> buttonW POST "Follow" followRoute
Just _ -> Just _ -> buttonW POST "Unfollow" unfollowRoute
[whamlet|
<div>[Following]
|]

View file

@ -1155,6 +1155,7 @@ instance ActivityPub Activity where
activityType (OfferActivity _) = "Offer" activityType (OfferActivity _) = "Offer"
activityType (PushActivity _) = "Push" activityType (PushActivity _) = "Push"
activityType (RejectActivity _) = "Reject" activityType (RejectActivity _) = "Reject"
activityType (UndoActivity _) = "Undo"
encodeSpecific h _ (AcceptActivity a) = encodeAccept h a encodeSpecific h _ (AcceptActivity a) = encodeAccept h a
encodeSpecific h u (CreateActivity a) = encodeCreate h u a encodeSpecific h u (CreateActivity a) = encodeCreate h u a
encodeSpecific _ _ (FollowActivity a) = encodeFollow a encodeSpecific _ _ (FollowActivity a) = encodeFollow a

View file

@ -115,9 +115,9 @@ library
Vervis.Access Vervis.Access
Vervis.ActivityPub Vervis.ActivityPub
Vervis.ActivityPub.Recipient
Vervis.ActorKey Vervis.ActorKey
Vervis.API Vervis.API
Vervis.API.Recipient
Vervis.Application Vervis.Application
Vervis.Avatar Vervis.Avatar
Vervis.BinaryBody Vervis.BinaryBody