Switch to new actor layout
This is such a huge patch, it's probably impossible to tell what it does by looking at the code. One thing is clear: It changes *everything* :P so here's an overview: - There are now 5 types of actors, each having its own top-level route - So projects, repos, etc. are no longer "under" sharers - Actor routes are now based on their KeyHashid, there are no "idents" anymore, i.e. URLs look random and don't contain user or repo names - No sharers anymore; people and groups are distinct entities not sharing a common namespace or anything like that - Project has been renamed to Deck and it simply means a ticket tracker; repos are no longer "under" projects - In addition to Person, Group, Repo and Deck, there's a new actor type Loom, which is a patch tracker; i.e. Repo actors don't manage MRs anymore - All C2S and S2S is temporarily disabled, because huge changes to the whole code are required and I'll do them gradually in the next patches - Since form-based actions are implemented using C2S, they're disabled as well, so Vervis is now essentially read-only - Some views have been temporarily removed, e.g. repo history and commit view - A huge set of DB migrations has been added to adapt the DB to these changes; I haven't tested them yet on a read DB so there may be errors there; I'll fix them in the next patches if I find any (probably going to test on the main instance where Vervis itself is hosted...) - Some modules got tech upgrades, e.g. LocalActor became a higher-kinded type and a similar pattern is probably relevant for several other types - There's an 'Actor' entity in the DB schema now, and all 5 actor types use it for common things like inbox and outbox - Although inbox and outbox are used only by Actor, so essentially could be removed, I haven't removed them; that's because I wonder if at some point users can have a tree of inboxes much like in email; I don't have an excuse for Outbox, but anyway, leaving them as is for now - Workflows, roles and collaborators are partially removed/unused until I figure out a sane federated way to provide these features - Since repo routes don't contain a "sharer" anymore, SSH URIs are now simpler, they already look like user@host/repo regardless of who "controls" that repo
This commit is contained in:
parent
91b2d36a19
commit
2e72684fd5
94 changed files with 8767 additions and 7728 deletions
33
migrations/303_2022-08-04_username.model
Normal file
33
migrations/303_2022-08-04_username.model
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
Inbox
|
||||||
|
Outbox
|
||||||
|
FollowerSet
|
||||||
|
|
||||||
|
Sharer
|
||||||
|
ident Text
|
||||||
|
name Text Maybe
|
||||||
|
created UTCTime
|
||||||
|
|
||||||
|
UniqueSharer ident
|
||||||
|
|
||||||
|
Person
|
||||||
|
ident SharerId
|
||||||
|
username Text
|
||||||
|
login Text
|
||||||
|
passphraseHash ByteString
|
||||||
|
email EmailAddress
|
||||||
|
verified Bool
|
||||||
|
verifiedKey Text
|
||||||
|
verifiedKeyCreated UTCTime
|
||||||
|
resetPassKey Text
|
||||||
|
resetPassKeyCreated UTCTime
|
||||||
|
about Text
|
||||||
|
inbox InboxId
|
||||||
|
outbox OutboxId
|
||||||
|
followers FollowerSetId
|
||||||
|
|
||||||
|
UniquePersonIdent ident
|
||||||
|
UniquePersonLogin login
|
||||||
|
UniquePersonEmail email
|
||||||
|
UniquePersonInbox inbox
|
||||||
|
UniquePersonOutbox outbox
|
||||||
|
UniquePersonFollowers followers
|
130
migrations/308_2022-08-04_remove_tcr.model
Normal file
130
migrations/308_2022-08-04_remove_tcr.model
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
Ticket
|
||||||
|
FollowerSet
|
||||||
|
RemoteActor
|
||||||
|
RemoteObject
|
||||||
|
Person
|
||||||
|
OutboxItem
|
||||||
|
RemoteActivity
|
||||||
|
Project
|
||||||
|
Repo
|
||||||
|
|
||||||
|
LocalTicket
|
||||||
|
ticket TicketId
|
||||||
|
discuss DiscussionId
|
||||||
|
followers FollowerSetId
|
||||||
|
|
||||||
|
UniqueLocalTicket ticket
|
||||||
|
UniqueLocalTicketDiscussion discuss
|
||||||
|
UniqueLocalTicketFollowers followers
|
||||||
|
|
||||||
|
TicketProjectRemote
|
||||||
|
ticket TicketAuthorLocalId
|
||||||
|
tracker RemoteActorId
|
||||||
|
project RemoteObjectId Maybe -- specify if not same as tracker
|
||||||
|
-- For MRs it may be either a remote repo or
|
||||||
|
-- a branch of it
|
||||||
|
|
||||||
|
UniqueTicketProjectRemote ticket
|
||||||
|
|
||||||
|
TicketAuthorLocal
|
||||||
|
ticket LocalTicketId
|
||||||
|
author PersonId
|
||||||
|
open OutboxItemId
|
||||||
|
|
||||||
|
UniqueTicketAuthorLocal ticket
|
||||||
|
UniqueTicketAuthorLocalOpen open
|
||||||
|
|
||||||
|
Message
|
||||||
|
created UTCTime
|
||||||
|
source Text -- Pandoc Markdown
|
||||||
|
content Text -- HTML
|
||||||
|
parent MessageId Maybe
|
||||||
|
root DiscussionId
|
||||||
|
|
||||||
|
LocalMessage
|
||||||
|
author PersonId
|
||||||
|
rest MessageId
|
||||||
|
create OutboxItemId
|
||||||
|
unlinkedParent FedURI Maybe
|
||||||
|
|
||||||
|
UniqueLocalMessage rest
|
||||||
|
UniqueLocalMessageCreate create
|
||||||
|
|
||||||
|
RemoteMessage
|
||||||
|
author RemoteActorId
|
||||||
|
ident RemoteObjectId
|
||||||
|
rest MessageId
|
||||||
|
create RemoteActivityId
|
||||||
|
lostParent FedURI Maybe
|
||||||
|
|
||||||
|
UniqueRemoteMessageIdent ident
|
||||||
|
UniqueRemoteMessage rest
|
||||||
|
UniqueRemoteMessageCreate create
|
||||||
|
|
||||||
|
Follow
|
||||||
|
person PersonId
|
||||||
|
target FollowerSetId
|
||||||
|
public Bool
|
||||||
|
follow OutboxItemId
|
||||||
|
accept OutboxItemId
|
||||||
|
|
||||||
|
UniqueFollow person target
|
||||||
|
UniqueFollowFollow follow
|
||||||
|
UniqueFollowAccept accept
|
||||||
|
|
||||||
|
RemoteFollow
|
||||||
|
actor RemoteActorId
|
||||||
|
target FollowerSetId
|
||||||
|
public Bool
|
||||||
|
follow RemoteActivityId
|
||||||
|
accept OutboxItemId
|
||||||
|
|
||||||
|
UniqueRemoteFollow actor target
|
||||||
|
UniqueRemoteFollowFollow follow
|
||||||
|
UniqueRemoteFollowAccept accept
|
||||||
|
|
||||||
|
RemoteTicket
|
||||||
|
ticket TicketAuthorRemoteId
|
||||||
|
ident RemoteObjectId
|
||||||
|
discuss RemoteDiscussionId
|
||||||
|
|
||||||
|
UniqueRemoteTicket ticket
|
||||||
|
UniqueRemoteTicketIdent ident
|
||||||
|
UniqueRemoteTicketDiscuss discuss
|
||||||
|
|
||||||
|
Discussion
|
||||||
|
|
||||||
|
RemoteDiscussion
|
||||||
|
ident RemoteObjectId
|
||||||
|
discuss DiscussionId
|
||||||
|
|
||||||
|
UniqueRemoteDiscussionIdent ident
|
||||||
|
UniqueRemoteDiscussion discuss
|
||||||
|
|
||||||
|
TicketAuthorRemote
|
||||||
|
ticket TicketContextLocalId
|
||||||
|
author RemoteActorId
|
||||||
|
open RemoteActivityId
|
||||||
|
|
||||||
|
UniqueTicketAuthorRemote ticket
|
||||||
|
UniqueTicketAuthorRemoteOpen open
|
||||||
|
|
||||||
|
TicketContextLocal
|
||||||
|
ticket TicketId
|
||||||
|
accept OutboxItemId
|
||||||
|
|
||||||
|
UniqueTicketContextLocal ticket
|
||||||
|
UniqueTicketContextLocalAccept accept
|
||||||
|
|
||||||
|
TicketProjectLocal
|
||||||
|
context TicketContextLocalId
|
||||||
|
project ProjectId
|
||||||
|
|
||||||
|
UniqueTicketProjectLocal context
|
||||||
|
|
||||||
|
TicketRepoLocal
|
||||||
|
context TicketContextLocalId
|
||||||
|
repo RepoId
|
||||||
|
branch Text Maybe
|
||||||
|
|
||||||
|
UniqueTicketRepoLocal context
|
23
migrations/310_2022-08-04_move_ticket_discuss.model
Normal file
23
migrations/310_2022-08-04_move_ticket_discuss.model
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
Person
|
||||||
|
FollowerSet
|
||||||
|
|
||||||
|
Discussion
|
||||||
|
|
||||||
|
Ticket
|
||||||
|
number Int Maybe
|
||||||
|
created UTCTime
|
||||||
|
title Text -- HTML
|
||||||
|
source Text -- Pandoc Markdown
|
||||||
|
description Text -- HTML
|
||||||
|
assignee PersonId Maybe
|
||||||
|
status TicketStatus
|
||||||
|
discuss DiscussionId
|
||||||
|
|
||||||
|
LocalTicket
|
||||||
|
ticket TicketId
|
||||||
|
discuss DiscussionId
|
||||||
|
followers FollowerSetId
|
||||||
|
|
||||||
|
UniqueLocalTicket ticket
|
||||||
|
UniqueLocalTicketDiscussion discuss
|
||||||
|
UniqueLocalTicketFollowers followers
|
26
migrations/312_2022-08-04_move_ticket_followers.model
Normal file
26
migrations/312_2022-08-04_move_ticket_followers.model
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
Person
|
||||||
|
Discussion
|
||||||
|
|
||||||
|
FollowerSet
|
||||||
|
|
||||||
|
Ticket
|
||||||
|
number Int Maybe
|
||||||
|
created UTCTime
|
||||||
|
title Text -- HTML
|
||||||
|
source Text -- Pandoc Markdown
|
||||||
|
description Text -- HTML
|
||||||
|
assignee PersonId Maybe
|
||||||
|
status TicketStatus
|
||||||
|
discuss DiscussionId
|
||||||
|
followers FollowerSetId
|
||||||
|
|
||||||
|
UniqueTicketDiscuss discuss
|
||||||
|
|
||||||
|
LocalTicket
|
||||||
|
ticket TicketId
|
||||||
|
discuss DiscussionId
|
||||||
|
followers FollowerSetId
|
||||||
|
|
||||||
|
UniqueLocalTicket ticket
|
||||||
|
UniqueLocalTicketDiscussion discuss
|
||||||
|
UniqueLocalTicketFollowers followers
|
32
migrations/316_2022-08-04_move_ticket_accept.model
Normal file
32
migrations/316_2022-08-04_move_ticket_accept.model
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
Person
|
||||||
|
Discussion
|
||||||
|
FollowerSet
|
||||||
|
|
||||||
|
Outbox
|
||||||
|
|
||||||
|
OutboxItem
|
||||||
|
outbox OutboxId
|
||||||
|
activity PersistJSONObject
|
||||||
|
published UTCTime
|
||||||
|
|
||||||
|
TicketContextLocal
|
||||||
|
ticket TicketId
|
||||||
|
accept OutboxItemId
|
||||||
|
|
||||||
|
UniqueTicketContextLocal ticket
|
||||||
|
UniqueTicketContextLocalAccept accept
|
||||||
|
|
||||||
|
Ticket
|
||||||
|
number Int Maybe
|
||||||
|
created UTCTime
|
||||||
|
title Text -- HTML
|
||||||
|
source Text -- Pandoc Markdown
|
||||||
|
description Text -- HTML
|
||||||
|
assignee PersonId Maybe
|
||||||
|
status TicketStatus
|
||||||
|
discuss DiscussionId
|
||||||
|
followers FollowerSetId
|
||||||
|
accept OutboxItemId
|
||||||
|
|
||||||
|
UniqueTicketDiscuss discuss
|
||||||
|
UniqueTicketFollowers followers
|
46
migrations/318_2022-08-04_tal_ticket.model
Normal file
46
migrations/318_2022-08-04_tal_ticket.model
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
Person
|
||||||
|
|
||||||
|
Discussion
|
||||||
|
|
||||||
|
FollowerSet
|
||||||
|
|
||||||
|
Outbox
|
||||||
|
|
||||||
|
OutboxItem
|
||||||
|
outbox OutboxId
|
||||||
|
activity PersistJSONObject
|
||||||
|
published UTCTime
|
||||||
|
|
||||||
|
Ticket
|
||||||
|
number Int Maybe
|
||||||
|
created UTCTime
|
||||||
|
title Text -- HTML
|
||||||
|
source Text -- Pandoc Markdown
|
||||||
|
description Text -- HTML
|
||||||
|
assignee PersonId Maybe
|
||||||
|
status TicketStatus
|
||||||
|
discuss DiscussionId
|
||||||
|
followers FollowerSetId
|
||||||
|
accept OutboxItemId
|
||||||
|
|
||||||
|
UniqueTicketDiscuss discuss
|
||||||
|
UniqueTicketFollowers followers
|
||||||
|
UniqueTicketAccept accept
|
||||||
|
|
||||||
|
TicketAuthorLocal
|
||||||
|
ticket LocalTicketId
|
||||||
|
ticketNew TicketId
|
||||||
|
author PersonId
|
||||||
|
open OutboxItemId
|
||||||
|
|
||||||
|
UniqueTicketAuthorLocal ticket
|
||||||
|
UniqueTicketAuthorLocalOpen open
|
||||||
|
|
||||||
|
LocalTicket
|
||||||
|
ticket TicketId
|
||||||
|
discuss DiscussionId
|
||||||
|
followers FollowerSetId
|
||||||
|
|
||||||
|
UniqueLocalTicket ticket
|
||||||
|
UniqueLocalTicketDiscussion discuss
|
||||||
|
UniqueLocalTicketFollowers followers
|
46
migrations/323_2022-08-04_tar_ticket.model
Normal file
46
migrations/323_2022-08-04_tar_ticket.model
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
Person
|
||||||
|
RemoteActor
|
||||||
|
RemoteActivity
|
||||||
|
|
||||||
|
Discussion
|
||||||
|
|
||||||
|
FollowerSet
|
||||||
|
|
||||||
|
Outbox
|
||||||
|
|
||||||
|
OutboxItem
|
||||||
|
outbox OutboxId
|
||||||
|
activity PersistJSONObject
|
||||||
|
published UTCTime
|
||||||
|
|
||||||
|
Ticket
|
||||||
|
number Int Maybe
|
||||||
|
created UTCTime
|
||||||
|
title Text -- HTML
|
||||||
|
source Text -- Pandoc Markdown
|
||||||
|
description Text -- HTML
|
||||||
|
assignee PersonId Maybe
|
||||||
|
status TicketStatus
|
||||||
|
discuss DiscussionId
|
||||||
|
followers FollowerSetId
|
||||||
|
accept OutboxItemId
|
||||||
|
|
||||||
|
UniqueTicketDiscuss discuss
|
||||||
|
UniqueTicketFollowers followers
|
||||||
|
UniqueTicketAccept accept
|
||||||
|
|
||||||
|
TicketAuthorRemote
|
||||||
|
ticket TicketContextLocalId
|
||||||
|
ticketNew TicketId
|
||||||
|
author RemoteActorId
|
||||||
|
open RemoteActivityId
|
||||||
|
|
||||||
|
UniqueTicketAuthorRemote ticket
|
||||||
|
UniqueTicketAuthorRemoteOpen open
|
||||||
|
|
||||||
|
TicketContextLocal
|
||||||
|
ticket TicketId
|
||||||
|
accept OutboxItemId
|
||||||
|
|
||||||
|
UniqueTicketContextLocal ticket
|
||||||
|
UniqueTicketContextLocalAccept accept
|
43
migrations/328_2022-08-04_tjl_ticket.model
Normal file
43
migrations/328_2022-08-04_tjl_ticket.model
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
Person
|
||||||
|
Project
|
||||||
|
|
||||||
|
Discussion
|
||||||
|
|
||||||
|
FollowerSet
|
||||||
|
|
||||||
|
Outbox
|
||||||
|
|
||||||
|
OutboxItem
|
||||||
|
outbox OutboxId
|
||||||
|
activity PersistJSONObject
|
||||||
|
published UTCTime
|
||||||
|
|
||||||
|
Ticket
|
||||||
|
number Int Maybe
|
||||||
|
created UTCTime
|
||||||
|
title Text -- HTML
|
||||||
|
source Text -- Pandoc Markdown
|
||||||
|
description Text -- HTML
|
||||||
|
assignee PersonId Maybe
|
||||||
|
status TicketStatus
|
||||||
|
discuss DiscussionId
|
||||||
|
followers FollowerSetId
|
||||||
|
accept OutboxItemId
|
||||||
|
|
||||||
|
UniqueTicketDiscuss discuss
|
||||||
|
UniqueTicketFollowers followers
|
||||||
|
UniqueTicketAccept accept
|
||||||
|
|
||||||
|
TicketProjectLocal
|
||||||
|
ticket TicketId
|
||||||
|
context TicketContextLocalId
|
||||||
|
project ProjectId
|
||||||
|
|
||||||
|
UniqueTicketProjectLocal context
|
||||||
|
|
||||||
|
TicketContextLocal
|
||||||
|
ticket TicketId
|
||||||
|
accept OutboxItemId
|
||||||
|
|
||||||
|
UniqueTicketContextLocal ticket
|
||||||
|
UniqueTicketContextLocalAccept accept
|
44
migrations/332_2022-08-04_trl_ticket.model
Normal file
44
migrations/332_2022-08-04_trl_ticket.model
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
Person
|
||||||
|
Repo
|
||||||
|
|
||||||
|
Discussion
|
||||||
|
|
||||||
|
FollowerSet
|
||||||
|
|
||||||
|
Outbox
|
||||||
|
|
||||||
|
OutboxItem
|
||||||
|
outbox OutboxId
|
||||||
|
activity PersistJSONObject
|
||||||
|
published UTCTime
|
||||||
|
|
||||||
|
Ticket
|
||||||
|
number Int Maybe
|
||||||
|
created UTCTime
|
||||||
|
title Text -- HTML
|
||||||
|
source Text -- Pandoc Markdown
|
||||||
|
description Text -- HTML
|
||||||
|
assignee PersonId Maybe
|
||||||
|
status TicketStatus
|
||||||
|
discuss DiscussionId
|
||||||
|
followers FollowerSetId
|
||||||
|
accept OutboxItemId
|
||||||
|
|
||||||
|
UniqueTicketDiscuss discuss
|
||||||
|
UniqueTicketFollowers followers
|
||||||
|
UniqueTicketAccept accept
|
||||||
|
|
||||||
|
TicketRepoLocal
|
||||||
|
ticket TicketId
|
||||||
|
context TicketContextLocalId
|
||||||
|
repo RepoId
|
||||||
|
branch Text Maybe
|
||||||
|
|
||||||
|
UniqueTicketRepoLocal context
|
||||||
|
|
||||||
|
TicketContextLocal
|
||||||
|
ticket TicketId
|
||||||
|
accept OutboxItemId
|
||||||
|
|
||||||
|
UniqueTicketContextLocal ticket
|
||||||
|
UniqueTicketContextLocalAccept accept
|
48
migrations/338_2022-08-04_rtd_child.model
Normal file
48
migrations/338_2022-08-04_rtd_child.model
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
Person
|
||||||
|
RemoteObject
|
||||||
|
RemoteActivity
|
||||||
|
|
||||||
|
Discussion
|
||||||
|
|
||||||
|
FollowerSet
|
||||||
|
|
||||||
|
Outbox
|
||||||
|
|
||||||
|
OutboxItem
|
||||||
|
outbox OutboxId
|
||||||
|
activity PersistJSONObject
|
||||||
|
published UTCTime
|
||||||
|
|
||||||
|
Ticket
|
||||||
|
number Int Maybe
|
||||||
|
created UTCTime
|
||||||
|
title Text -- HTML
|
||||||
|
source Text -- Pandoc Markdown
|
||||||
|
description Text -- HTML
|
||||||
|
assignee PersonId Maybe
|
||||||
|
status TicketStatus
|
||||||
|
discuss DiscussionId
|
||||||
|
followers FollowerSetId
|
||||||
|
accept OutboxItemId
|
||||||
|
|
||||||
|
UniqueTicketDiscuss discuss
|
||||||
|
UniqueTicketFollowers followers
|
||||||
|
UniqueTicketAccept accept
|
||||||
|
|
||||||
|
RemoteTicketDependency
|
||||||
|
ident RemoteObjectId
|
||||||
|
child LocalTicketId
|
||||||
|
childNew TicketId
|
||||||
|
accept RemoteActivityId
|
||||||
|
|
||||||
|
UniqueRemoteTicketDependency ident
|
||||||
|
UniqueRemoteTicketDependencyAccept accept
|
||||||
|
|
||||||
|
LocalTicket
|
||||||
|
ticket TicketId
|
||||||
|
discuss DiscussionId
|
||||||
|
followers FollowerSetId
|
||||||
|
|
||||||
|
UniqueLocalTicket ticket
|
||||||
|
UniqueLocalTicketDiscussion discuss
|
||||||
|
UniqueLocalTicketFollowers followers
|
45
migrations/342_2022-08-04_ltd_parent.model
Normal file
45
migrations/342_2022-08-04_ltd_parent.model
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
Person
|
||||||
|
|
||||||
|
Discussion
|
||||||
|
|
||||||
|
FollowerSet
|
||||||
|
|
||||||
|
Outbox
|
||||||
|
|
||||||
|
OutboxItem
|
||||||
|
outbox OutboxId
|
||||||
|
activity PersistJSONObject
|
||||||
|
published UTCTime
|
||||||
|
|
||||||
|
Ticket
|
||||||
|
number Int Maybe
|
||||||
|
created UTCTime
|
||||||
|
title Text -- HTML
|
||||||
|
source Text -- Pandoc Markdown
|
||||||
|
description Text -- HTML
|
||||||
|
assignee PersonId Maybe
|
||||||
|
status TicketStatus
|
||||||
|
discuss DiscussionId
|
||||||
|
followers FollowerSetId
|
||||||
|
accept OutboxItemId
|
||||||
|
|
||||||
|
UniqueTicketDiscuss discuss
|
||||||
|
UniqueTicketFollowers followers
|
||||||
|
UniqueTicketAccept accept
|
||||||
|
|
||||||
|
LocalTicketDependency
|
||||||
|
parent LocalTicketId
|
||||||
|
parentNew TicketId
|
||||||
|
created UTCTime
|
||||||
|
accept OutboxItemId
|
||||||
|
|
||||||
|
UniqueLocalTicketDependencyAccept accept
|
||||||
|
|
||||||
|
LocalTicket
|
||||||
|
ticket TicketId
|
||||||
|
discuss DiscussionId
|
||||||
|
followers FollowerSetId
|
||||||
|
|
||||||
|
UniqueLocalTicket ticket
|
||||||
|
UniqueLocalTicketDiscussion discuss
|
||||||
|
UniqueLocalTicketFollowers followers
|
45
migrations/345_2022-08-04_tdcl_child.model
Normal file
45
migrations/345_2022-08-04_tdcl_child.model
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
Person
|
||||||
|
LocalTicketDependency
|
||||||
|
|
||||||
|
Discussion
|
||||||
|
|
||||||
|
FollowerSet
|
||||||
|
|
||||||
|
Outbox
|
||||||
|
|
||||||
|
OutboxItem
|
||||||
|
outbox OutboxId
|
||||||
|
activity PersistJSONObject
|
||||||
|
published UTCTime
|
||||||
|
|
||||||
|
Ticket
|
||||||
|
number Int Maybe
|
||||||
|
created UTCTime
|
||||||
|
title Text -- HTML
|
||||||
|
source Text -- Pandoc Markdown
|
||||||
|
description Text -- HTML
|
||||||
|
assignee PersonId Maybe
|
||||||
|
status TicketStatus
|
||||||
|
discuss DiscussionId
|
||||||
|
followers FollowerSetId
|
||||||
|
accept OutboxItemId
|
||||||
|
|
||||||
|
UniqueTicketDiscuss discuss
|
||||||
|
UniqueTicketFollowers followers
|
||||||
|
UniqueTicketAccept accept
|
||||||
|
|
||||||
|
TicketDependencyChildLocal
|
||||||
|
dep LocalTicketDependencyId
|
||||||
|
child LocalTicketId
|
||||||
|
childNew TicketId
|
||||||
|
|
||||||
|
UniqueTicketDependencyChildLocal dep
|
||||||
|
|
||||||
|
LocalTicket
|
||||||
|
ticket TicketId
|
||||||
|
discuss DiscussionId
|
||||||
|
followers FollowerSetId
|
||||||
|
|
||||||
|
UniqueLocalTicket ticket
|
||||||
|
UniqueLocalTicketDiscussion discuss
|
||||||
|
UniqueLocalTicketFollowers followers
|
45
migrations/348_2022-08-04_tr_ticket.model
Normal file
45
migrations/348_2022-08-04_tr_ticket.model
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
Person
|
||||||
|
|
||||||
|
Discussion
|
||||||
|
|
||||||
|
FollowerSet
|
||||||
|
|
||||||
|
Outbox
|
||||||
|
|
||||||
|
OutboxItem
|
||||||
|
outbox OutboxId
|
||||||
|
activity PersistJSONObject
|
||||||
|
published UTCTime
|
||||||
|
|
||||||
|
Ticket
|
||||||
|
number Int Maybe
|
||||||
|
created UTCTime
|
||||||
|
title Text -- HTML
|
||||||
|
source Text -- Pandoc Markdown
|
||||||
|
description Text -- HTML
|
||||||
|
assignee PersonId Maybe
|
||||||
|
status TicketStatus
|
||||||
|
discuss DiscussionId
|
||||||
|
followers FollowerSetId
|
||||||
|
accept OutboxItemId
|
||||||
|
|
||||||
|
UniqueTicketDiscuss discuss
|
||||||
|
UniqueTicketFollowers followers
|
||||||
|
UniqueTicketAccept accept
|
||||||
|
|
||||||
|
TicketResolve
|
||||||
|
ticket LocalTicketId
|
||||||
|
ticketNew TicketId
|
||||||
|
accept OutboxItemId
|
||||||
|
|
||||||
|
UniqueTicketResolve ticket
|
||||||
|
UniqueTicketResolveAccept accept
|
||||||
|
|
||||||
|
LocalTicket
|
||||||
|
ticket TicketId
|
||||||
|
discuss DiscussionId
|
||||||
|
followers FollowerSetId
|
||||||
|
|
||||||
|
UniqueLocalTicket ticket
|
||||||
|
UniqueLocalTicketDiscussion discuss
|
||||||
|
UniqueLocalTicketFollowers followers
|
41
migrations/356_2022-08-04_person_actor.model
Normal file
41
migrations/356_2022-08-04_person_actor.model
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
Outbox
|
||||||
|
|
||||||
|
Inbox
|
||||||
|
|
||||||
|
FollowerSet
|
||||||
|
|
||||||
|
Actor
|
||||||
|
name Text
|
||||||
|
desc Text
|
||||||
|
createdAt UTCTime
|
||||||
|
inbox InboxId
|
||||||
|
outbox OutboxId
|
||||||
|
followers FollowerSetId
|
||||||
|
|
||||||
|
UniqueActorInbox inbox
|
||||||
|
UniqueActorOutbox outbox
|
||||||
|
UniqueActorFollowers followers
|
||||||
|
|
||||||
|
Person
|
||||||
|
username Username
|
||||||
|
login Text
|
||||||
|
passphraseHash ByteString
|
||||||
|
email EmailAddress
|
||||||
|
verified Bool
|
||||||
|
verifiedKey Text
|
||||||
|
verifiedKeyCreated UTCTime
|
||||||
|
resetPassKey Text
|
||||||
|
resetPassKeyCreated UTCTime
|
||||||
|
about Text
|
||||||
|
actor ActorId
|
||||||
|
inbox InboxId
|
||||||
|
outbox OutboxId
|
||||||
|
followers FollowerSetId
|
||||||
|
-- reviewFollow Bool
|
||||||
|
|
||||||
|
UniquePersonUsername username
|
||||||
|
UniquePersonLogin login
|
||||||
|
UniquePersonEmail email
|
||||||
|
UniquePersonInbox inbox
|
||||||
|
UniquePersonOutbox outbox
|
||||||
|
UniquePersonFollowers followers
|
30
migrations/365_2022-08-04_group_actor.model
Normal file
30
migrations/365_2022-08-04_group_actor.model
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
Inbox
|
||||||
|
|
||||||
|
Outbox
|
||||||
|
|
||||||
|
FollowerSet
|
||||||
|
|
||||||
|
Group
|
||||||
|
ident SharerId
|
||||||
|
actor ActorId
|
||||||
|
|
||||||
|
UniqueGroup ident
|
||||||
|
|
||||||
|
Sharer
|
||||||
|
ident ShrIdent
|
||||||
|
name Text Maybe
|
||||||
|
created UTCTime
|
||||||
|
|
||||||
|
UniqueSharer ident
|
||||||
|
|
||||||
|
Actor
|
||||||
|
name Text
|
||||||
|
desc Text
|
||||||
|
createdAt UTCTime
|
||||||
|
inbox InboxId
|
||||||
|
outbox OutboxId
|
||||||
|
followers FollowerSetId
|
||||||
|
|
||||||
|
UniqueActorInbox inbox
|
||||||
|
UniqueActorOutbox outbox
|
||||||
|
UniqueActorFollowers followers
|
40
migrations/367_2022-08-04_repo_actor.model
Normal file
40
migrations/367_2022-08-04_repo_actor.model
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
Deck
|
||||||
|
Role
|
||||||
|
Sharer
|
||||||
|
|
||||||
|
Inbox
|
||||||
|
|
||||||
|
Outbox
|
||||||
|
|
||||||
|
FollowerSet
|
||||||
|
|
||||||
|
Actor
|
||||||
|
name Text
|
||||||
|
desc Text
|
||||||
|
createdAt UTCTime
|
||||||
|
inbox InboxId
|
||||||
|
outbox OutboxId
|
||||||
|
followers FollowerSetId
|
||||||
|
|
||||||
|
UniqueActorInbox inbox
|
||||||
|
UniqueActorOutbox outbox
|
||||||
|
UniqueActorFollowers followers
|
||||||
|
|
||||||
|
Repo
|
||||||
|
vcs VersionControlSystem
|
||||||
|
project DeckId Maybe
|
||||||
|
mainBranch Text
|
||||||
|
collabUser RoleId Maybe
|
||||||
|
collabAnon RoleId Maybe
|
||||||
|
actor ActorId
|
||||||
|
desc Text Maybe
|
||||||
|
ident RpIdent
|
||||||
|
inbox InboxId
|
||||||
|
outbox OutboxId
|
||||||
|
followers FollowerSetId
|
||||||
|
sharer SharerId
|
||||||
|
|
||||||
|
UniqueRepo ident sharer
|
||||||
|
UniqueRepoInbox inbox
|
||||||
|
UniqueRepoOutbox outbox
|
||||||
|
UniqueRepoFollowers followers
|
9
migrations/384_2022-08-04_loom.model
Normal file
9
migrations/384_2022-08-04_loom.model
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
Loom
|
||||||
|
nextTicket Int
|
||||||
|
actor ActorId
|
||||||
|
repo RepoId
|
||||||
|
create OutboxItemId
|
||||||
|
|
||||||
|
UniqueLoomActor actor
|
||||||
|
UniqueLoomRepo repo
|
||||||
|
UniqueLoomCreate create
|
5
migrations/386_2022-08-04_assignee.model
Normal file
5
migrations/386_2022-08-04_assignee.model
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
TicketAssignee
|
||||||
|
ticket TicketId
|
||||||
|
person PersonId
|
||||||
|
|
||||||
|
UniqueTicketAssignee ticket person
|
129
migrations/388_2022-08-04_ticket_loom.model
Normal file
129
migrations/388_2022-08-04_ticket_loom.model
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
Repo
|
||||||
|
OutboxItem
|
||||||
|
Person
|
||||||
|
RemoteActor
|
||||||
|
RemoteObject
|
||||||
|
RemoteActivity
|
||||||
|
|
||||||
|
TicketRepoLocal
|
||||||
|
ticket TicketId
|
||||||
|
repo RepoId
|
||||||
|
branch Text Maybe
|
||||||
|
|
||||||
|
UniqueTicketRepoLocal ticket
|
||||||
|
|
||||||
|
Ticket
|
||||||
|
number Int Maybe
|
||||||
|
created UTCTime
|
||||||
|
title Text -- HTML
|
||||||
|
source Text -- Pandoc Markdown
|
||||||
|
description Text -- HTML
|
||||||
|
status TicketStatus
|
||||||
|
discuss DiscussionId
|
||||||
|
followers FollowerSetId
|
||||||
|
accept OutboxItemId
|
||||||
|
|
||||||
|
-- UniqueTicket project number
|
||||||
|
UniqueTicketDiscuss discuss
|
||||||
|
UniqueTicketFollowers followers
|
||||||
|
UniqueTicketAccept accept
|
||||||
|
|
||||||
|
Message
|
||||||
|
created UTCTime
|
||||||
|
source Text -- Pandoc Markdown
|
||||||
|
content Text -- HTML
|
||||||
|
parent MessageId Maybe
|
||||||
|
root DiscussionId
|
||||||
|
|
||||||
|
LocalMessage
|
||||||
|
author PersonId
|
||||||
|
rest MessageId
|
||||||
|
create OutboxItemId
|
||||||
|
unlinkedParent FedURI Maybe
|
||||||
|
|
||||||
|
UniqueLocalMessage rest
|
||||||
|
UniqueLocalMessageCreate create
|
||||||
|
|
||||||
|
RemoteMessage
|
||||||
|
author RemoteActorId
|
||||||
|
ident RemoteObjectId
|
||||||
|
rest MessageId
|
||||||
|
create RemoteActivityId
|
||||||
|
lostParent FedURI Maybe
|
||||||
|
|
||||||
|
UniqueRemoteMessageIdent ident
|
||||||
|
UniqueRemoteMessage rest
|
||||||
|
UniqueRemoteMessageCreate create
|
||||||
|
|
||||||
|
Bundle
|
||||||
|
ticket TicketId
|
||||||
|
|
||||||
|
Patch
|
||||||
|
bundle BundleId
|
||||||
|
created UTCTime
|
||||||
|
type PatchMediaType
|
||||||
|
content Text
|
||||||
|
|
||||||
|
TicketResolve
|
||||||
|
ticket TicketId
|
||||||
|
accept OutboxItemId
|
||||||
|
|
||||||
|
UniqueTicketResolve ticket
|
||||||
|
UniqueTicketResolveAccept accept
|
||||||
|
|
||||||
|
TicketResolveLocal
|
||||||
|
ticket TicketResolveId
|
||||||
|
activity OutboxItemId
|
||||||
|
|
||||||
|
UniqueTicketResolveLocal ticket
|
||||||
|
UniqueTicketResolveLocalActivity activity
|
||||||
|
|
||||||
|
TicketResolveRemote
|
||||||
|
ticket TicketResolveId
|
||||||
|
activity RemoteActivityId
|
||||||
|
actor RemoteActorId
|
||||||
|
|
||||||
|
UniqueTicketResolveRemote ticket
|
||||||
|
UniqueTicketResolveRemoteActivity activity
|
||||||
|
|
||||||
|
Discussion
|
||||||
|
|
||||||
|
FollowerSet
|
||||||
|
|
||||||
|
Follow
|
||||||
|
person PersonId
|
||||||
|
target FollowerSetId
|
||||||
|
public Bool
|
||||||
|
follow OutboxItemId
|
||||||
|
accept OutboxItemId
|
||||||
|
|
||||||
|
UniqueFollow person target
|
||||||
|
UniqueFollowFollow follow
|
||||||
|
UniqueFollowAccept accept
|
||||||
|
|
||||||
|
RemoteFollow
|
||||||
|
actor RemoteActorId
|
||||||
|
target FollowerSetId
|
||||||
|
public Bool
|
||||||
|
follow RemoteActivityId
|
||||||
|
accept OutboxItemId
|
||||||
|
|
||||||
|
UniqueRemoteFollow actor target
|
||||||
|
UniqueRemoteFollowFollow follow
|
||||||
|
UniqueRemoteFollowAccept accept
|
||||||
|
|
||||||
|
TicketAuthorLocal
|
||||||
|
ticket TicketId
|
||||||
|
author PersonId
|
||||||
|
open OutboxItemId
|
||||||
|
|
||||||
|
UniqueTicketAuthorLocal ticket
|
||||||
|
UniqueTicketAuthorLocalOpen open
|
||||||
|
|
||||||
|
TicketAuthorRemote
|
||||||
|
ticket TicketId
|
||||||
|
author RemoteActorId
|
||||||
|
open RemoteActivityId
|
||||||
|
|
||||||
|
UniqueTicketAuthorRemote ticket
|
||||||
|
UniqueTicketAuthorRemoteOpen open
|
36
migrations/396_2022-08-04_repo_dir.model
Normal file
36
migrations/396_2022-08-04_repo_dir.model
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
Deck
|
||||||
|
Role
|
||||||
|
OutboxItem
|
||||||
|
Outbox
|
||||||
|
Inbox
|
||||||
|
FollowerSet
|
||||||
|
|
||||||
|
Repo
|
||||||
|
vcs VersionControlSystem
|
||||||
|
project DeckId Maybe
|
||||||
|
mainBranch Text
|
||||||
|
collabUser RoleId Maybe
|
||||||
|
collabAnon RoleId Maybe
|
||||||
|
actor ActorId
|
||||||
|
sharer SharerId
|
||||||
|
|
||||||
|
UniqueRepoActor actor
|
||||||
|
|
||||||
|
Sharer
|
||||||
|
ident ShrIdent
|
||||||
|
name Text Maybe
|
||||||
|
created UTCTime
|
||||||
|
|
||||||
|
UniqueSharer ident
|
||||||
|
|
||||||
|
Actor
|
||||||
|
name Text
|
||||||
|
desc Text
|
||||||
|
createdAt UTCTime
|
||||||
|
inbox InboxId
|
||||||
|
outbox OutboxId
|
||||||
|
followers FollowerSetId
|
||||||
|
|
||||||
|
UniqueActorInbox inbox
|
||||||
|
UniqueActorOutbox outbox
|
||||||
|
UniqueActorFollowers followers
|
17
migrations/399_2022-08-04_fwder.model
Normal file
17
migrations/399_2022-08-04_fwder.model
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
ForwarderPerson
|
||||||
|
task ForwardingId
|
||||||
|
sender PersonId
|
||||||
|
|
||||||
|
UniqueForwarderPerson task
|
||||||
|
|
||||||
|
ForwarderGroup
|
||||||
|
task ForwardingId
|
||||||
|
sender GroupId
|
||||||
|
|
||||||
|
UniqueForwarderGroup task
|
||||||
|
|
||||||
|
ForwarderLoom
|
||||||
|
task ForwardingId
|
||||||
|
sender LoomId
|
||||||
|
|
||||||
|
UniqueForwarderLoom task
|
5
migrations/408_2022-08-04_collab_loom.model
Normal file
5
migrations/408_2022-08-04_collab_loom.model
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
CollabTopicLocalLoom
|
||||||
|
collab CollabId
|
||||||
|
loom LoomId
|
||||||
|
|
||||||
|
UniqueCollabTopicLocalLoom collab
|
52
migrations/409_2022-08-05_repo_create.model
Normal file
52
migrations/409_2022-08-05_repo_create.model
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
Deck
|
||||||
|
Role
|
||||||
|
Inbox
|
||||||
|
FollowerSet
|
||||||
|
|
||||||
|
Actor
|
||||||
|
name Text
|
||||||
|
desc Text
|
||||||
|
createdAt UTCTime
|
||||||
|
inbox InboxId
|
||||||
|
outbox OutboxId
|
||||||
|
followers FollowerSetId
|
||||||
|
|
||||||
|
UniqueActorInbox inbox
|
||||||
|
UniqueActorOutbox outbox
|
||||||
|
UniqueActorFollowers followers
|
||||||
|
|
||||||
|
Outbox
|
||||||
|
|
||||||
|
OutboxItem
|
||||||
|
outbox OutboxId
|
||||||
|
activity PersistJSONObject
|
||||||
|
published UTCTime
|
||||||
|
|
||||||
|
Repo
|
||||||
|
vcs VersionControlSystem
|
||||||
|
project DeckId Maybe
|
||||||
|
mainBranch Text
|
||||||
|
collabUser RoleId Maybe
|
||||||
|
collabAnon RoleId Maybe
|
||||||
|
actor ActorId
|
||||||
|
create OutboxItemId
|
||||||
|
|
||||||
|
UniqueRepoActor actor
|
||||||
|
|
||||||
|
Person
|
||||||
|
username Username
|
||||||
|
login Text
|
||||||
|
passphraseHash ByteString
|
||||||
|
email EmailAddress
|
||||||
|
verified Bool
|
||||||
|
verifiedKey Text
|
||||||
|
verifiedKeyCreated UTCTime
|
||||||
|
resetPassKey Text
|
||||||
|
resetPassKeyCreated UTCTime
|
||||||
|
actor ActorId
|
||||||
|
-- reviewFollow Bool
|
||||||
|
|
||||||
|
UniquePersonUsername username
|
||||||
|
UniquePersonLogin login
|
||||||
|
UniquePersonEmail email
|
||||||
|
UniquePersonActor actor
|
52
migrations/414_2022-08-05_followremote_actor.model
Normal file
52
migrations/414_2022-08-05_followremote_actor.model
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
RemoteActor
|
||||||
|
OutboxItem
|
||||||
|
RemoteActivity
|
||||||
|
|
||||||
|
Inbox
|
||||||
|
|
||||||
|
Outbox
|
||||||
|
|
||||||
|
FollowerSet
|
||||||
|
|
||||||
|
Actor
|
||||||
|
name Text
|
||||||
|
desc Text
|
||||||
|
createdAt UTCTime
|
||||||
|
inbox InboxId
|
||||||
|
outbox OutboxId
|
||||||
|
followers FollowerSetId
|
||||||
|
|
||||||
|
UniqueActorInbox inbox
|
||||||
|
UniqueActorOutbox outbox
|
||||||
|
UniqueActorFollowers followers
|
||||||
|
|
||||||
|
FollowRemote
|
||||||
|
person PersonId
|
||||||
|
actor ActorId
|
||||||
|
recip RemoteActorId -- actor managing the followed object
|
||||||
|
target FedURI -- the followed object
|
||||||
|
public Bool
|
||||||
|
follow OutboxItemId
|
||||||
|
accept RemoteActivityId
|
||||||
|
|
||||||
|
UniqueFollowRemote person target
|
||||||
|
UniqueFollowRemoteFollow follow
|
||||||
|
UniqueFollowRemoteAccept accept
|
||||||
|
|
||||||
|
Person
|
||||||
|
username Username
|
||||||
|
login Text
|
||||||
|
passphraseHash ByteString
|
||||||
|
email EmailAddress
|
||||||
|
verified Bool
|
||||||
|
verifiedKey Text
|
||||||
|
verifiedKeyCreated UTCTime
|
||||||
|
resetPassKey Text
|
||||||
|
resetPassKeyCreated UTCTime
|
||||||
|
actor ActorId
|
||||||
|
-- reviewFollow Bool
|
||||||
|
|
||||||
|
UniquePersonUsername username
|
||||||
|
UniquePersonLogin login
|
||||||
|
UniquePersonEmail email
|
||||||
|
UniquePersonActor actor
|
49
migrations/418_2022-08-06_follow_actor.model
Normal file
49
migrations/418_2022-08-06_follow_actor.model
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
OutboxItem
|
||||||
|
|
||||||
|
Inbox
|
||||||
|
|
||||||
|
Outbox
|
||||||
|
|
||||||
|
FollowerSet
|
||||||
|
|
||||||
|
Actor
|
||||||
|
name Text
|
||||||
|
desc Text
|
||||||
|
createdAt UTCTime
|
||||||
|
inbox InboxId
|
||||||
|
outbox OutboxId
|
||||||
|
followers FollowerSetId
|
||||||
|
|
||||||
|
UniqueActorInbox inbox
|
||||||
|
UniqueActorOutbox outbox
|
||||||
|
UniqueActorFollowers followers
|
||||||
|
|
||||||
|
Follow
|
||||||
|
person PersonId
|
||||||
|
actor ActorId -- TODO CONTINUE write a migration adding this field
|
||||||
|
target FollowerSetId
|
||||||
|
public Bool
|
||||||
|
follow OutboxItemId
|
||||||
|
accept OutboxItemId
|
||||||
|
|
||||||
|
UniqueFollow person target
|
||||||
|
UniqueFollowFollow follow
|
||||||
|
UniqueFollowAccept accept
|
||||||
|
|
||||||
|
Person
|
||||||
|
username Username
|
||||||
|
login Text
|
||||||
|
passphraseHash ByteString
|
||||||
|
email EmailAddress
|
||||||
|
verified Bool
|
||||||
|
verifiedKey Text
|
||||||
|
verifiedKeyCreated UTCTime
|
||||||
|
resetPassKey Text
|
||||||
|
resetPassKeyCreated UTCTime
|
||||||
|
actor ActorId
|
||||||
|
-- reviewFollow Bool
|
||||||
|
|
||||||
|
UniquePersonUsername username
|
||||||
|
UniquePersonLogin login
|
||||||
|
UniquePersonEmail email
|
||||||
|
UniquePersonActor actor
|
|
@ -1,6 +1,6 @@
|
||||||
{- This file is part of Vervis.
|
{- This file is part of Vervis.
|
||||||
-
|
-
|
||||||
- Written in 2016, 2019 by fr33domlover <fr33domlover@riseup.net>.
|
- Written in 2016, 2019, 2022 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.
|
||||||
-
|
-
|
||||||
|
@ -34,17 +34,17 @@ import qualified Data.ByteString as B
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import qualified Data.Text.IO as TIO
|
import qualified Data.Text.IO as TIO
|
||||||
|
|
||||||
writeDefaultsFile :: FilePath -> FilePath -> Text -> Text -> Text -> IO ()
|
writeDefaultsFile :: FilePath -> FilePath -> Text -> Text -> IO ()
|
||||||
writeDefaultsFile path cmd authority sharer repo = do
|
writeDefaultsFile path cmd authority repo = do
|
||||||
let file = path </> "_darcs" </> "prefs" </> "defaults"
|
let file = path </> "_darcs" </> "prefs" </> "defaults"
|
||||||
TIO.writeFile file $ defaultsContent cmd authority sharer repo
|
TIO.writeFile file $ defaultsContent cmd authority repo
|
||||||
setFileMode file $ ownerReadMode .|. ownerWriteMode
|
setFileMode file $ ownerReadMode .|. ownerWriteMode
|
||||||
where
|
where
|
||||||
defaultsContent :: FilePath -> Text -> Text -> Text -> Text
|
defaultsContent :: FilePath -> Text -> Text -> Text
|
||||||
defaultsContent hook authority sharer repo =
|
defaultsContent hook authority repo =
|
||||||
T.concat
|
T.concat
|
||||||
[ "apply posthook "
|
[ "apply posthook "
|
||||||
, T.pack hook, " ", authority, " ", sharer, " ", repo
|
, T.pack hook, " ", authority, " ", repo
|
||||||
]
|
]
|
||||||
|
|
||||||
{-
|
{-
|
||||||
|
@ -80,18 +80,16 @@ createRepo
|
||||||
-> Text
|
-> Text
|
||||||
-- ^ Instance HTTP authority
|
-- ^ Instance HTTP authority
|
||||||
-> Text
|
-> Text
|
||||||
-- ^ Repo sharer textual ID
|
-- ^ Repo key hashid
|
||||||
-> Text
|
|
||||||
-- ^ Repo textual ID
|
|
||||||
-> IO ()
|
-> IO ()
|
||||||
createRepo parent name cmd authority sharer repo = do
|
createRepo parent name cmd authority repo = do
|
||||||
let path = parent </> name
|
let path = parent </> name
|
||||||
createDirectory path
|
createDirectory path
|
||||||
let settings = proc "darcs" ["init", "--no-working-dir", "--repodir", path]
|
let settings = proc "darcs" ["init", "--no-working-dir", "--repodir", path]
|
||||||
(_, _, _, ph) <- createProcess settings
|
(_, _, _, ph) <- createProcess settings
|
||||||
ec <- waitForProcess ph
|
ec <- waitForProcess ph
|
||||||
case ec of
|
case ec of
|
||||||
ExitSuccess -> writeDefaultsFile path cmd authority sharer repo
|
ExitSuccess -> writeDefaultsFile path cmd authority repo
|
||||||
ExitFailure n -> error $ "darcs init failed with exit code " ++ show n
|
ExitFailure n -> error $ "darcs init failed with exit code " ++ show n
|
||||||
|
|
||||||
readPristineRoot :: FilePath -> IO (Maybe Int, Hash)
|
readPristineRoot :: FilePath -> IO (Maybe Int, Hash)
|
||||||
|
|
|
@ -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, 2022 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.
|
||||||
-
|
-
|
||||||
|
@ -54,21 +54,21 @@ instance SpecToEventTime GitTime where
|
||||||
specToEventTime = specToEventTime . gitTimeUTC
|
specToEventTime = specToEventTime . gitTimeUTC
|
||||||
specsToEventTimes = specsToEventTimes . fmap gitTimeUTC
|
specsToEventTimes = specsToEventTimes . fmap gitTimeUTC
|
||||||
|
|
||||||
hookContent :: FilePath -> Text -> Text -> Text -> Text
|
hookContent :: FilePath -> Text -> Text -> Text
|
||||||
hookContent hook authority sharer repo =
|
hookContent hook authority repo =
|
||||||
T.concat
|
T.concat
|
||||||
[ "#!/bin/sh\nexec ", T.pack hook
|
[ "#!/bin/sh\nexec ", T.pack hook
|
||||||
, " ", authority, " ", sharer, " ", repo
|
, " ", authority, " ", repo
|
||||||
]
|
]
|
||||||
|
|
||||||
writeHookFile :: FilePath -> FilePath -> Text -> Text -> Text -> IO ()
|
writeHookFile :: FilePath -> FilePath -> Text -> Text -> IO ()
|
||||||
writeHookFile path cmd authority sharer repo = do
|
writeHookFile path cmd authority repo = do
|
||||||
let file = path </> "hooks" </> "post-receive"
|
let file = path </> "hooks" </> "post-receive"
|
||||||
TIO.writeFile file $ hookContent cmd authority sharer repo
|
TIO.writeFile file $ hookContent cmd authority repo
|
||||||
setFileMode file ownerModes
|
setFileMode file ownerModes
|
||||||
|
|
||||||
initialRepoTree :: FilePath -> Text -> Text -> Text -> FileName -> DirTree Text
|
initialRepoTree :: FilePath -> Text -> Text -> FileName -> DirTree Text
|
||||||
initialRepoTree hook authority sharer repo dir =
|
initialRepoTree hook authority repo dir =
|
||||||
Dir dir
|
Dir dir
|
||||||
[ Dir "branches" []
|
[ Dir "branches" []
|
||||||
, File "config"
|
, File "config"
|
||||||
|
@ -80,7 +80,7 @@ initialRepoTree hook authority sharer repo dir =
|
||||||
"Unnamed repository; edit this file to name the repository."
|
"Unnamed repository; edit this file to name the repository."
|
||||||
, File "HEAD" "ref: refs/heads/master"
|
, File "HEAD" "ref: refs/heads/master"
|
||||||
, Dir "hooks"
|
, Dir "hooks"
|
||||||
[ File "post-receive" $ hookContent hook authority sharer repo
|
[ File "post-receive" $ hookContent hook authority repo
|
||||||
]
|
]
|
||||||
, Dir "info"
|
, Dir "info"
|
||||||
[ File "exclude" ""
|
[ File "exclude" ""
|
||||||
|
@ -110,12 +110,10 @@ createRepo
|
||||||
-> Text
|
-> Text
|
||||||
-- ^ Instance HTTP authority
|
-- ^ Instance HTTP authority
|
||||||
-> Text
|
-> Text
|
||||||
-- ^ Repo sharer textual ID
|
-- ^ Repo hashid
|
||||||
-> Text
|
|
||||||
-- ^ Repo textual ID
|
|
||||||
-> IO ()
|
-> IO ()
|
||||||
createRepo path name cmd authority sharer repo = do
|
createRepo path name cmd authority repo = do
|
||||||
let tree = path :/ initialRepoTree cmd authority sharer repo name
|
let tree = path :/ initialRepoTree cmd authority repo name
|
||||||
result <- writeDirectoryWith TIO.writeFile tree
|
result <- writeDirectoryWith TIO.writeFile tree
|
||||||
let errs = failures $ dirTree result
|
let errs = failures $ dirTree result
|
||||||
when (not . null $ errs) $
|
when (not . null $ errs) $
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{- This file is part of Vervis.
|
{- This file is part of Vervis.
|
||||||
-
|
-
|
||||||
- Written in 2019, 2020 by fr33domlover <fr33domlover@riseup.net>.
|
- Written in 2019, 2020, 2022 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.
|
||||||
-
|
-
|
||||||
|
@ -21,6 +21,7 @@ module Database.Persist.Local
|
||||||
, insertUnique_
|
, insertUnique_
|
||||||
, insertBy'
|
, insertBy'
|
||||||
, insertByEntity'
|
, insertByEntity'
|
||||||
|
, getE
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
|
@ -28,6 +29,8 @@ import Control.Applicative
|
||||||
import Control.Exception
|
import Control.Exception
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
import Control.Monad.IO.Class
|
import Control.Monad.IO.Class
|
||||||
|
import Control.Monad.Trans.Class
|
||||||
|
import Control.Monad.Trans.Except
|
||||||
import Control.Monad.Trans.Maybe
|
import Control.Monad.Trans.Maybe
|
||||||
import Control.Monad.Trans.Reader
|
import Control.Monad.Trans.Reader
|
||||||
import Data.Bifunctor
|
import Data.Bifunctor
|
||||||
|
@ -95,3 +98,15 @@ insertByEntity'
|
||||||
)
|
)
|
||||||
=> record -> ReaderT backend m (Either (Entity record) (Entity record))
|
=> record -> ReaderT backend m (Either (Entity record) (Entity record))
|
||||||
insertByEntity' val = second (flip Entity val) <$> insertBy' val
|
insertByEntity' val = second (flip Entity val) <$> insertBy' val
|
||||||
|
|
||||||
|
getE
|
||||||
|
:: ( PersistStoreRead backend
|
||||||
|
, MonadIO m
|
||||||
|
, PersistRecordBackend record backend
|
||||||
|
)
|
||||||
|
=> Key record -> e -> ExceptT e (ReaderT backend m) record
|
||||||
|
getE key msg = do
|
||||||
|
mval <- lift $ get key
|
||||||
|
case mval of
|
||||||
|
Nothing -> throwE msg
|
||||||
|
Just val -> return val
|
||||||
|
|
1599
src/Vervis/API.hs
1599
src/Vervis/API.hs
File diff suppressed because it is too large
Load diff
|
@ -54,6 +54,7 @@
|
||||||
-- operations.
|
-- operations.
|
||||||
module Vervis.Access
|
module Vervis.Access
|
||||||
( ObjectAccessStatus (..)
|
( ObjectAccessStatus (..)
|
||||||
|
, checkRepoAccess'
|
||||||
, checkRepoAccess
|
, checkRepoAccess
|
||||||
, checkProjectAccess
|
, checkProjectAccess
|
||||||
)
|
)
|
||||||
|
@ -64,12 +65,15 @@ import Control.Monad.IO.Class
|
||||||
import Control.Monad.Trans.Maybe
|
import Control.Monad.Trans.Maybe
|
||||||
import Control.Monad.Trans.Reader
|
import Control.Monad.Trans.Reader
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Database.Persist.Class (getBy)
|
import Database.Persist.Class
|
||||||
import Database.Persist.Sql (SqlBackend)
|
import Database.Persist.Sql (SqlBackend)
|
||||||
import Database.Persist.Types (Entity (..))
|
import Database.Persist.Types (Entity (..))
|
||||||
|
|
||||||
import qualified Database.Esqueleto as E
|
import qualified Database.Esqueleto as E
|
||||||
|
|
||||||
|
import Yesod.Hashids
|
||||||
|
import Yesod.MonadSite
|
||||||
|
|
||||||
import Vervis.Model
|
import Vervis.Model
|
||||||
import Vervis.Model.Ident
|
import Vervis.Model.Ident
|
||||||
import Vervis.Model.Role
|
import Vervis.Model.Role
|
||||||
|
@ -114,17 +118,53 @@ status :: Bool -> ObjectAccessStatus
|
||||||
status True = ObjectAccessAllowed
|
status True = ObjectAccessAllowed
|
||||||
status False = ObjectAccessDenied
|
status False = ObjectAccessDenied
|
||||||
|
|
||||||
checkRepoAccess
|
checkRepoAccess'
|
||||||
:: MonadIO m
|
:: MonadIO m
|
||||||
=> Maybe PersonId
|
=> Maybe PersonId
|
||||||
-> ProjectOperation
|
-> ProjectOperation
|
||||||
-> ShrIdent
|
-> RepoId
|
||||||
-> RpIdent
|
|
||||||
-> ReaderT SqlBackend m ObjectAccessStatus
|
-> ReaderT SqlBackend m ObjectAccessStatus
|
||||||
checkRepoAccess mpid op shr rp = do
|
checkRepoAccess' mpid op repoID = do
|
||||||
mer <- runMaybeT $ do
|
mer <- runMaybeT $ do
|
||||||
Entity sid _sharer <- MaybeT $ getBy $ UniqueSharer shr
|
repo <- MaybeT $ get repoID
|
||||||
MaybeT $ getBy $ UniqueRepo rp sid
|
return $ Entity repoID repo
|
||||||
|
case mer of
|
||||||
|
Nothing -> return NoSuchObject
|
||||||
|
Just (Entity rid repo) -> do
|
||||||
|
role <- do
|
||||||
|
case mpid of
|
||||||
|
Just pid ->
|
||||||
|
fromMaybe User . (<|> asUser repo) <$> asCollab rid pid
|
||||||
|
Nothing -> pure $ fromMaybe Guest $ asAnon repo
|
||||||
|
status <$> roleHasAccess role op
|
||||||
|
where
|
||||||
|
asCollab rid pid = do
|
||||||
|
fmap (maybe Developer RoleID . E.unValue . snd) . listToMaybe <$> do
|
||||||
|
E.select $ E.from $ \ (topic `E.InnerJoin` recip `E.LeftOuterJoin` role) -> do
|
||||||
|
E.on $ E.just (topic E.^. CollabTopicLocalRepoCollab) E.==. role E.?. CollabRoleLocalCollab
|
||||||
|
E.on $ topic E.^. CollabTopicLocalRepoCollab E.==. recip E.^. CollabRecipLocalCollab
|
||||||
|
E.where_ $
|
||||||
|
topic E.^. CollabTopicLocalRepoRepo E.==. E.val rid E.&&.
|
||||||
|
recip E.^. CollabRecipLocalPerson E.==. E.val pid
|
||||||
|
E.limit 1
|
||||||
|
return
|
||||||
|
( topic E.^. CollabTopicLocalRepoCollab
|
||||||
|
, role E.?. CollabRoleLocalRole
|
||||||
|
)
|
||||||
|
asUser = fmap RoleID . repoCollabUser
|
||||||
|
asAnon = fmap RoleID . repoCollabAnon
|
||||||
|
|
||||||
|
checkRepoAccess
|
||||||
|
:: (MonadIO m, MonadSite m, YesodHashids (SiteEnv m))
|
||||||
|
=> Maybe PersonId
|
||||||
|
-> ProjectOperation
|
||||||
|
-> KeyHashid Repo
|
||||||
|
-> ReaderT SqlBackend m ObjectAccessStatus
|
||||||
|
checkRepoAccess mpid op repoHash = do
|
||||||
|
mer <- runMaybeT $ do
|
||||||
|
repoID <- decodeKeyHashidM repoHash
|
||||||
|
repo <- MaybeT $ get repoID
|
||||||
|
return $ Entity repoID repo
|
||||||
case mer of
|
case mer of
|
||||||
Nothing -> return NoSuchObject
|
Nothing -> return NoSuchObject
|
||||||
Just (Entity rid repo) -> do
|
Just (Entity rid repo) -> do
|
||||||
|
@ -152,16 +192,16 @@ checkRepoAccess mpid op shr rp = do
|
||||||
asAnon = fmap RoleID . repoCollabAnon
|
asAnon = fmap RoleID . repoCollabAnon
|
||||||
|
|
||||||
checkProjectAccess
|
checkProjectAccess
|
||||||
:: MonadIO m
|
:: (MonadIO m, MonadSite m, YesodHashids (SiteEnv m))
|
||||||
=> Maybe PersonId
|
=> Maybe PersonId
|
||||||
-> ProjectOperation
|
-> ProjectOperation
|
||||||
-> ShrIdent
|
-> KeyHashid Deck
|
||||||
-> PrjIdent
|
|
||||||
-> ReaderT SqlBackend m ObjectAccessStatus
|
-> ReaderT SqlBackend m ObjectAccessStatus
|
||||||
checkProjectAccess mpid op shr prj = do
|
checkProjectAccess mpid op deckHash = do
|
||||||
mej <- runMaybeT $ do
|
mej <- runMaybeT $ do
|
||||||
Entity sid _sharer <- MaybeT $ getBy $ UniqueSharer shr
|
deckID <- decodeKeyHashidM deckHash
|
||||||
MaybeT $ getBy $ UniqueProject prj sid
|
deck <- MaybeT $ get deckID
|
||||||
|
return $ Entity deckID deck
|
||||||
case mej of
|
case mej of
|
||||||
Nothing -> return NoSuchObject
|
Nothing -> return NoSuchObject
|
||||||
Just (Entity jid project) -> do
|
Just (Entity jid project) -> do
|
||||||
|
@ -176,15 +216,15 @@ checkProjectAccess mpid op shr prj = do
|
||||||
asCollab jid pid = do
|
asCollab jid pid = do
|
||||||
fmap (maybe Developer RoleID . E.unValue . snd) . listToMaybe <$> do
|
fmap (maybe Developer RoleID . E.unValue . snd) . listToMaybe <$> do
|
||||||
E.select $ E.from $ \ (topic `E.InnerJoin` recip `E.LeftOuterJoin` role) -> do
|
E.select $ E.from $ \ (topic `E.InnerJoin` recip `E.LeftOuterJoin` role) -> do
|
||||||
E.on $ E.just (topic E.^. CollabTopicLocalProjectCollab) E.==. role E.?. CollabRoleLocalCollab
|
E.on $ E.just (topic E.^. CollabTopicLocalDeckCollab) E.==. role E.?. CollabRoleLocalCollab
|
||||||
E.on $ topic E.^. CollabTopicLocalProjectCollab E.==. recip E.^. CollabRecipLocalCollab
|
E.on $ topic E.^. CollabTopicLocalDeckCollab E.==. recip E.^. CollabRecipLocalCollab
|
||||||
E.where_ $
|
E.where_ $
|
||||||
topic E.^. CollabTopicLocalProjectProject E.==. E.val jid E.&&.
|
topic E.^. CollabTopicLocalDeckDeck E.==. E.val jid E.&&.
|
||||||
recip E.^. CollabRecipLocalPerson E.==. E.val pid
|
recip E.^. CollabRecipLocalPerson E.==. E.val pid
|
||||||
E.limit 1
|
E.limit 1
|
||||||
return
|
return
|
||||||
( topic E.^. CollabTopicLocalProjectCollab
|
( topic E.^. CollabTopicLocalDeckCollab
|
||||||
, role E.?. CollabRoleLocalRole
|
, role E.?. CollabRoleLocalRole
|
||||||
)
|
)
|
||||||
asUser = fmap RoleID . projectCollabUser
|
asUser = fmap RoleID . deckCollabUser
|
||||||
asAnon = fmap RoleID . projectCollabAnon
|
asAnon = fmap RoleID . deckCollabAnon
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,654 +0,0 @@
|
||||||
{- This file is part of Vervis.
|
|
||||||
-
|
|
||||||
- Written in 2019, 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.ActivityPub.Recipient
|
|
||||||
( LocalActor (..)
|
|
||||||
, LocalPersonCollection (..)
|
|
||||||
, LocalTicketDirectSet (..)
|
|
||||||
, LocalPatchDirectSet (..)
|
|
||||||
, LocalProjectDirectSet (..)
|
|
||||||
, LocalProjectRelatedSet (..)
|
|
||||||
, LocalRepoDirectSet (..)
|
|
||||||
, LocalRepoRelatedSet (..)
|
|
||||||
, LocalSharerDirectSet (..)
|
|
||||||
, LocalSharerRelatedSet (..)
|
|
||||||
, LocalRecipientSet
|
|
||||||
, concatRecipients
|
|
||||||
, parseLocalActor
|
|
||||||
, renderLocalActor
|
|
||||||
, renderLocalPersonCollection
|
|
||||||
, makeRecipientSet
|
|
||||||
, ParsedAudience (..)
|
|
||||||
, parseAudience
|
|
||||||
, actorRecips
|
|
||||||
, localRecipSieve
|
|
||||||
, localRecipSieve'
|
|
||||||
|
|
||||||
, Aud (..)
|
|
||||||
, collectAudience
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Control.Applicative
|
|
||||||
import Control.Monad
|
|
||||||
import Control.Monad.Trans.Except
|
|
||||||
import Data.Bifunctor
|
|
||||||
import Data.Either
|
|
||||||
import Data.Foldable
|
|
||||||
import Data.List ((\\))
|
|
||||||
import Data.List.NonEmpty (NonEmpty, nonEmpty)
|
|
||||||
import Data.Maybe
|
|
||||||
import Data.Semigroup
|
|
||||||
import Data.Text (Text)
|
|
||||||
import Data.These
|
|
||||||
import Data.Traversable
|
|
||||||
|
|
||||||
import qualified Data.List.NonEmpty as NE
|
|
||||||
import qualified Data.List.Ordered as LO
|
|
||||||
import qualified Data.Text as T
|
|
||||||
|
|
||||||
import Network.FedURI
|
|
||||||
import Web.ActivityPub hiding (Ticket)
|
|
||||||
import Yesod.ActivityPub
|
|
||||||
import Yesod.FedURI
|
|
||||||
import Yesod.Hashids
|
|
||||||
import Yesod.MonadSite
|
|
||||||
|
|
||||||
import Data.List.Local
|
|
||||||
import Data.List.NonEmpty.Local
|
|
||||||
|
|
||||||
import Vervis.FedURI
|
|
||||||
import Vervis.Foundation
|
|
||||||
import Vervis.Model
|
|
||||||
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
|
|
||||||
--
|
|
||||||
-- These are the 2 kinds of local recipients. This is the starting point for
|
|
||||||
-- grouping and checking recipient lists: First parse recipient URIs into these
|
|
||||||
-- types, then you can do any further parsing and grouping.
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
data LocalActor
|
|
||||||
= LocalActorSharer ShrIdent
|
|
||||||
| LocalActorProject ShrIdent PrjIdent
|
|
||||||
| LocalActorRepo ShrIdent RpIdent
|
|
||||||
deriving (Eq, Ord)
|
|
||||||
|
|
||||||
parseLocalActor :: Route App -> Maybe LocalActor
|
|
||||||
parseLocalActor (SharerR shr) = Just $ LocalActorSharer shr
|
|
||||||
parseLocalActor (ProjectR shr prj) = Just $ LocalActorProject shr prj
|
|
||||||
parseLocalActor (RepoR shr rp) = Just $ LocalActorRepo shr rp
|
|
||||||
parseLocalActor _ = Nothing
|
|
||||||
|
|
||||||
renderLocalActor :: LocalActor -> Route App
|
|
||||||
renderLocalActor (LocalActorSharer shr) = SharerR shr
|
|
||||||
renderLocalActor (LocalActorProject shr prj) = ProjectR shr prj
|
|
||||||
renderLocalActor (LocalActorRepo shr rp) = RepoR shr rp
|
|
||||||
|
|
||||||
data LocalPersonCollection
|
|
||||||
= LocalPersonCollectionSharerFollowers ShrIdent
|
|
||||||
| LocalPersonCollectionSharerTicketTeam ShrIdent (KeyHashid TicketAuthorLocal)
|
|
||||||
| LocalPersonCollectionSharerTicketFollowers ShrIdent (KeyHashid TicketAuthorLocal)
|
|
||||||
| LocalPersonCollectionSharerProposalFollowers ShrIdent (KeyHashid TicketAuthorLocal)
|
|
||||||
|
|
||||||
| LocalPersonCollectionProjectTeam ShrIdent PrjIdent
|
|
||||||
| LocalPersonCollectionProjectFollowers ShrIdent PrjIdent
|
|
||||||
| LocalPersonCollectionProjectTicketTeam ShrIdent PrjIdent (KeyHashid LocalTicket)
|
|
||||||
| LocalPersonCollectionProjectTicketFollowers ShrIdent PrjIdent (KeyHashid LocalTicket)
|
|
||||||
|
|
||||||
| LocalPersonCollectionRepoTeam ShrIdent RpIdent
|
|
||||||
| LocalPersonCollectionRepoFollowers ShrIdent RpIdent
|
|
||||||
| LocalPersonCollectionRepoProposalFollowers ShrIdent RpIdent (KeyHashid LocalTicket)
|
|
||||||
deriving (Eq, Ord)
|
|
||||||
|
|
||||||
parseLocalPersonCollection
|
|
||||||
:: Route App -> Maybe LocalPersonCollection
|
|
||||||
parseLocalPersonCollection (SharerFollowersR shr) =
|
|
||||||
Just $ LocalPersonCollectionSharerFollowers shr
|
|
||||||
parseLocalPersonCollection (SharerTicketTeamR shr talkhid) =
|
|
||||||
Just $ LocalPersonCollectionSharerTicketTeam shr talkhid
|
|
||||||
parseLocalPersonCollection (SharerTicketFollowersR shr talkhid) =
|
|
||||||
Just $ LocalPersonCollectionSharerTicketFollowers shr talkhid
|
|
||||||
parseLocalPersonCollection (SharerProposalFollowersR shr talkhid) =
|
|
||||||
Just $ LocalPersonCollectionSharerProposalFollowers shr talkhid
|
|
||||||
parseLocalPersonCollection (ProjectTeamR shr prj) =
|
|
||||||
Just $ LocalPersonCollectionProjectTeam shr prj
|
|
||||||
parseLocalPersonCollection (ProjectFollowersR shr prj) =
|
|
||||||
Just $ LocalPersonCollectionProjectFollowers shr prj
|
|
||||||
parseLocalPersonCollection (ProjectTicketTeamR shr prj num) =
|
|
||||||
Just $ LocalPersonCollectionProjectTicketTeam shr prj num
|
|
||||||
parseLocalPersonCollection (ProjectTicketParticipantsR shr prj num) =
|
|
||||||
Just $ LocalPersonCollectionProjectTicketFollowers shr prj num
|
|
||||||
parseLocalPersonCollection (RepoTeamR shr rp) =
|
|
||||||
Just $ LocalPersonCollectionRepoTeam shr rp
|
|
||||||
parseLocalPersonCollection (RepoFollowersR shr rp) =
|
|
||||||
Just $ LocalPersonCollectionRepoFollowers shr rp
|
|
||||||
parseLocalPersonCollection (RepoProposalFollowersR shr rp ltkhid) =
|
|
||||||
Just $ LocalPersonCollectionRepoProposalFollowers shr rp ltkhid
|
|
||||||
parseLocalPersonCollection _ = Nothing
|
|
||||||
|
|
||||||
renderLocalPersonCollection :: LocalPersonCollection -> Route App
|
|
||||||
renderLocalPersonCollection (LocalPersonCollectionSharerFollowers shr) = SharerFollowersR shr
|
|
||||||
renderLocalPersonCollection (LocalPersonCollectionSharerTicketTeam shr talkhid) = SharerTicketTeamR shr talkhid
|
|
||||||
renderLocalPersonCollection (LocalPersonCollectionSharerTicketFollowers shr talkhid) = SharerTicketFollowersR shr talkhid
|
|
||||||
renderLocalPersonCollection (LocalPersonCollectionSharerProposalFollowers shr talkhid) = SharerProposalFollowersR shr talkhid
|
|
||||||
renderLocalPersonCollection (LocalPersonCollectionProjectTeam shr prj) = ProjectTeamR shr prj
|
|
||||||
renderLocalPersonCollection (LocalPersonCollectionProjectFollowers shr prj) = ProjectFollowersR shr prj
|
|
||||||
renderLocalPersonCollection (LocalPersonCollectionProjectTicketTeam shr prj ltkhid) = ProjectTicketTeamR shr prj ltkhid
|
|
||||||
renderLocalPersonCollection (LocalPersonCollectionProjectTicketFollowers shr prj ltkhid) = ProjectTicketParticipantsR shr prj ltkhid
|
|
||||||
renderLocalPersonCollection (LocalPersonCollectionRepoTeam shr rp) = RepoTeamR shr rp
|
|
||||||
renderLocalPersonCollection (LocalPersonCollectionRepoFollowers shr rp) = RepoFollowersR shr rp
|
|
||||||
renderLocalPersonCollection (LocalPersonCollectionRepoProposalFollowers shr rp ltkhid) = RepoProposalFollowersR shr rp ltkhid
|
|
||||||
|
|
||||||
parseLocalRecipient
|
|
||||||
:: Route App -> Maybe (Either LocalActor LocalPersonCollection)
|
|
||||||
parseLocalRecipient r =
|
|
||||||
Left <$> parseLocalActor r <|> Right <$> parseLocalPersonCollection r
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
-- Intermediate recipient types
|
|
||||||
--
|
|
||||||
-- These are here just to help with grouping recipients. From this
|
|
||||||
-- representation it's easy to group recipients into a form that is friendly to
|
|
||||||
-- the code that fetches the actual recipients from the DB.
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
data LocalTicketRecipientDirect = LocalTicketTeam | LocalTicketFollowerz
|
|
||||||
deriving (Eq, Ord)
|
|
||||||
|
|
||||||
data LocalPatchRecipientDirect = LocalPatchFollowers deriving (Eq, Ord)
|
|
||||||
|
|
||||||
data LocalProjectRecipientDirect
|
|
||||||
= LocalProject
|
|
||||||
| LocalProjectTeam
|
|
||||||
| LocalProjectFollowers
|
|
||||||
deriving (Eq, Ord)
|
|
||||||
|
|
||||||
data LocalProjectRecipient
|
|
||||||
= LocalProjectDirect LocalProjectRecipientDirect
|
|
||||||
| LocalProjectTicketRelated (KeyHashid LocalTicket) LocalTicketRecipientDirect
|
|
||||||
deriving (Eq, Ord)
|
|
||||||
|
|
||||||
data LocalRepoRecipientDirect
|
|
||||||
= LocalRepo
|
|
||||||
| LocalRepoTeam
|
|
||||||
| LocalRepoFollowers
|
|
||||||
deriving (Eq, Ord)
|
|
||||||
|
|
||||||
data LocalRepoRecipient
|
|
||||||
= LocalRepoDirect LocalRepoRecipientDirect
|
|
||||||
| LocalRepoProposalRelated (KeyHashid LocalTicket) LocalPatchRecipientDirect
|
|
||||||
deriving (Eq, Ord)
|
|
||||||
|
|
||||||
data LocalSharerRecipientDirect
|
|
||||||
= LocalSharer
|
|
||||||
| LocalSharerFollowers
|
|
||||||
deriving (Eq, Ord)
|
|
||||||
|
|
||||||
data LocalSharerRecipient
|
|
||||||
= LocalSharerDirect LocalSharerRecipientDirect
|
|
||||||
| LocalSharerTicketRelated (KeyHashid TicketAuthorLocal) LocalTicketRecipientDirect
|
|
||||||
| LocalSharerProposalRelated (KeyHashid TicketAuthorLocal) LocalPatchRecipientDirect
|
|
||||||
| LocalProjectRelated PrjIdent LocalProjectRecipient
|
|
||||||
| LocalRepoRelated RpIdent LocalRepoRecipient
|
|
||||||
deriving (Eq, Ord)
|
|
||||||
|
|
||||||
data LocalGroupedRecipient = LocalSharerRelated ShrIdent LocalSharerRecipient
|
|
||||||
deriving (Eq, Ord)
|
|
||||||
|
|
||||||
groupedRecipientFromActor :: LocalActor -> LocalGroupedRecipient
|
|
||||||
groupedRecipientFromActor (LocalActorSharer shr) =
|
|
||||||
LocalSharerRelated shr $ LocalSharerDirect LocalSharer
|
|
||||||
groupedRecipientFromActor (LocalActorProject shr prj) =
|
|
||||||
LocalSharerRelated shr $ LocalProjectRelated prj $
|
|
||||||
LocalProjectDirect LocalProject
|
|
||||||
groupedRecipientFromActor (LocalActorRepo shr rp) =
|
|
||||||
LocalSharerRelated shr $ LocalRepoRelated rp $ LocalRepoDirect LocalRepo
|
|
||||||
|
|
||||||
groupedRecipientFromCollection
|
|
||||||
:: LocalPersonCollection -> LocalGroupedRecipient
|
|
||||||
groupedRecipientFromCollection
|
|
||||||
(LocalPersonCollectionSharerFollowers shr) =
|
|
||||||
LocalSharerRelated shr $ LocalSharerDirect LocalSharerFollowers
|
|
||||||
groupedRecipientFromCollection
|
|
||||||
(LocalPersonCollectionSharerTicketTeam shr talkhid) =
|
|
||||||
LocalSharerRelated shr $
|
|
||||||
LocalSharerTicketRelated talkhid LocalTicketTeam
|
|
||||||
groupedRecipientFromCollection
|
|
||||||
(LocalPersonCollectionSharerTicketFollowers shr talkhid) =
|
|
||||||
LocalSharerRelated shr $
|
|
||||||
LocalSharerTicketRelated talkhid LocalTicketFollowerz
|
|
||||||
groupedRecipientFromCollection
|
|
||||||
(LocalPersonCollectionSharerProposalFollowers shr talkhid) =
|
|
||||||
LocalSharerRelated shr $
|
|
||||||
LocalSharerProposalRelated talkhid LocalPatchFollowers
|
|
||||||
groupedRecipientFromCollection
|
|
||||||
(LocalPersonCollectionProjectTeam shr prj) =
|
|
||||||
LocalSharerRelated shr $ LocalProjectRelated prj $
|
|
||||||
LocalProjectDirect LocalProjectTeam
|
|
||||||
groupedRecipientFromCollection
|
|
||||||
(LocalPersonCollectionProjectFollowers shr prj) =
|
|
||||||
LocalSharerRelated shr $ LocalProjectRelated prj $
|
|
||||||
LocalProjectDirect LocalProjectFollowers
|
|
||||||
groupedRecipientFromCollection
|
|
||||||
(LocalPersonCollectionProjectTicketTeam shr prj num) =
|
|
||||||
LocalSharerRelated shr $ LocalProjectRelated prj $
|
|
||||||
LocalProjectTicketRelated num LocalTicketTeam
|
|
||||||
groupedRecipientFromCollection
|
|
||||||
(LocalPersonCollectionProjectTicketFollowers shr prj num) =
|
|
||||||
LocalSharerRelated shr $ LocalProjectRelated prj $
|
|
||||||
LocalProjectTicketRelated num LocalTicketFollowerz
|
|
||||||
groupedRecipientFromCollection
|
|
||||||
(LocalPersonCollectionRepoTeam shr rp) =
|
|
||||||
LocalSharerRelated shr $ LocalRepoRelated rp $
|
|
||||||
LocalRepoDirect LocalRepoTeam
|
|
||||||
groupedRecipientFromCollection
|
|
||||||
(LocalPersonCollectionRepoFollowers shr rp) =
|
|
||||||
LocalSharerRelated shr $ LocalRepoRelated rp $
|
|
||||||
LocalRepoDirect LocalRepoFollowers
|
|
||||||
groupedRecipientFromCollection
|
|
||||||
(LocalPersonCollectionRepoProposalFollowers shr rp ltkhid) =
|
|
||||||
LocalSharerRelated shr $ LocalRepoRelated rp $
|
|
||||||
LocalRepoProposalRelated ltkhid LocalPatchFollowers
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
-- Recipient set types
|
|
||||||
--
|
|
||||||
-- These types represent a set of recipients grouped by the variable components
|
|
||||||
-- of their routes. It's convenient to use when looking for the recipients in
|
|
||||||
-- the DB, and easy to manipulate and check the recipient list in terms of app
|
|
||||||
-- logic rather than plain lists of routes.
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
data LocalTicketDirectSet = LocalTicketDirectSet
|
|
||||||
{ localRecipTicketTeam :: Bool
|
|
||||||
, localRecipTicketFollowers :: Bool
|
|
||||||
}
|
|
||||||
deriving Eq
|
|
||||||
|
|
||||||
newtype LocalPatchDirectSet = LocalPatchDirectSet
|
|
||||||
{ localRecipPatchFollowers :: Bool
|
|
||||||
}
|
|
||||||
deriving Eq
|
|
||||||
|
|
||||||
data LocalProjectDirectSet = LocalProjectDirectSet
|
|
||||||
{ localRecipProject :: Bool
|
|
||||||
, localRecipProjectTeam :: Bool
|
|
||||||
, localRecipProjectFollowers :: Bool
|
|
||||||
}
|
|
||||||
deriving Eq
|
|
||||||
|
|
||||||
data LocalProjectRelatedSet = LocalProjectRelatedSet
|
|
||||||
{ localRecipProjectDirect
|
|
||||||
:: LocalProjectDirectSet
|
|
||||||
, localRecipProjectTicketRelated
|
|
||||||
:: [(KeyHashid LocalTicket, LocalTicketDirectSet)]
|
|
||||||
}
|
|
||||||
deriving Eq
|
|
||||||
|
|
||||||
data LocalRepoDirectSet = LocalRepoDirectSet
|
|
||||||
{ localRecipRepo :: Bool
|
|
||||||
, localRecipRepoTeam :: Bool
|
|
||||||
, localRecipRepoFollowers :: Bool
|
|
||||||
}
|
|
||||||
deriving Eq
|
|
||||||
|
|
||||||
data LocalRepoRelatedSet = LocalRepoRelatedSet
|
|
||||||
{ localRecipRepoDirect
|
|
||||||
:: LocalRepoDirectSet
|
|
||||||
, localRecipRepoProposalRelated
|
|
||||||
:: [(KeyHashid LocalTicket, LocalPatchDirectSet)]
|
|
||||||
}
|
|
||||||
deriving Eq
|
|
||||||
|
|
||||||
data LocalSharerDirectSet = LocalSharerDirectSet
|
|
||||||
{ localRecipSharer :: Bool
|
|
||||||
, localRecipSharerFollowers :: Bool
|
|
||||||
}
|
|
||||||
deriving Eq
|
|
||||||
|
|
||||||
data LocalSharerRelatedSet = LocalSharerRelatedSet
|
|
||||||
{ localRecipSharerDirect
|
|
||||||
:: LocalSharerDirectSet
|
|
||||||
, localRecipSharerTicketRelated
|
|
||||||
:: [(KeyHashid TicketAuthorLocal, LocalTicketDirectSet)]
|
|
||||||
, localRecipSharerProposalRelated
|
|
||||||
:: [(KeyHashid TicketAuthorLocal, LocalPatchDirectSet)]
|
|
||||||
, localRecipProjectRelated
|
|
||||||
:: [(PrjIdent, LocalProjectRelatedSet)]
|
|
||||||
, localRecipRepoRelated
|
|
||||||
:: [(RpIdent, LocalRepoRelatedSet)]
|
|
||||||
}
|
|
||||||
deriving Eq
|
|
||||||
|
|
||||||
type LocalRecipientSet = [(ShrIdent, LocalSharerRelatedSet)]
|
|
||||||
|
|
||||||
groupLocalRecipients :: [LocalGroupedRecipient] -> LocalRecipientSet
|
|
||||||
groupLocalRecipients
|
|
||||||
= map (second lsr2set)
|
|
||||||
. groupAllExtract
|
|
||||||
(\ (LocalSharerRelated shr _) -> shr)
|
|
||||||
(\ (LocalSharerRelated _ lsr) -> lsr)
|
|
||||||
where
|
|
||||||
lsr2set = mk . partitionLSR . NE.toList
|
|
||||||
where
|
|
||||||
partitionLSR = foldr f ([], [], [], [], [])
|
|
||||||
where
|
|
||||||
f i (ds, ts, ps, js, rs) =
|
|
||||||
case i of
|
|
||||||
LocalSharerDirect d ->
|
|
||||||
(d:ds, ts, ps, js, rs)
|
|
||||||
LocalSharerTicketRelated talkhid ltr ->
|
|
||||||
(ds, (talkhid, ltr):ts, ps, js, rs)
|
|
||||||
LocalSharerProposalRelated talkhid lpr ->
|
|
||||||
(ds, ts, (talkhid, lpr):ps, js, rs)
|
|
||||||
LocalProjectRelated prj ljr ->
|
|
||||||
(ds, ts, ps, (prj, ljr):js, rs)
|
|
||||||
LocalRepoRelated rp lrr ->
|
|
||||||
(ds, ts, ps, js, (rp, lrr):rs)
|
|
||||||
mk (ds, ts, ps, js, rs) =
|
|
||||||
LocalSharerRelatedSet
|
|
||||||
(lsrs2set ds)
|
|
||||||
(map (second ltrs2set) $ groupWithExtract fst snd ts)
|
|
||||||
(map (second lprs2set) $ groupWithExtract fst snd ps)
|
|
||||||
(map (second ljr2set) $ groupWithExtract fst snd js)
|
|
||||||
(map (second lrr2set) $ groupWithExtract fst snd rs)
|
|
||||||
where
|
|
||||||
lsrs2set = foldl' f initial
|
|
||||||
where
|
|
||||||
initial = LocalSharerDirectSet False False
|
|
||||||
f s LocalSharer =
|
|
||||||
s { localRecipSharer = True }
|
|
||||||
f s LocalSharerFollowers =
|
|
||||||
s { localRecipSharerFollowers = True }
|
|
||||||
ltrs2set = foldl' f initial
|
|
||||||
where
|
|
||||||
initial = LocalTicketDirectSet False False
|
|
||||||
f s LocalTicketTeam =
|
|
||||||
s { localRecipTicketTeam = True }
|
|
||||||
f s LocalTicketFollowerz =
|
|
||||||
s { localRecipTicketFollowers = True }
|
|
||||||
lprs2set = foldl' f initial
|
|
||||||
where
|
|
||||||
initial = LocalPatchDirectSet False
|
|
||||||
f s LocalPatchFollowers = s { localRecipPatchFollowers = True }
|
|
||||||
ljr2set = uncurry mk . partitionEithers . map ljr2e . NE.toList
|
|
||||||
where
|
|
||||||
ljr2e (LocalProjectDirect d) = Left d
|
|
||||||
ljr2e (LocalProjectTicketRelated num ltrs) = Right (num, ltrs)
|
|
||||||
mk ds ts =
|
|
||||||
LocalProjectRelatedSet
|
|
||||||
(ljrs2set ds)
|
|
||||||
(map (second ltrs2set) $ groupWithExtract fst snd ts)
|
|
||||||
where
|
|
||||||
ljrs2set = foldl' f initial
|
|
||||||
where
|
|
||||||
initial = LocalProjectDirectSet False False False
|
|
||||||
f s LocalProject =
|
|
||||||
s { localRecipProject = True }
|
|
||||||
f s LocalProjectTeam =
|
|
||||||
s { localRecipProjectTeam = True }
|
|
||||||
f s LocalProjectFollowers =
|
|
||||||
s { localRecipProjectFollowers = True }
|
|
||||||
lrr2set = uncurry mk . partitionEithers . map lrr2e . NE.toList
|
|
||||||
where
|
|
||||||
lrr2e (LocalRepoDirect d) = Left d
|
|
||||||
lrr2e (LocalRepoProposalRelated num ltrs) = Right (num, ltrs)
|
|
||||||
mk ds ps =
|
|
||||||
LocalRepoRelatedSet
|
|
||||||
(lrrs2set ds)
|
|
||||||
(map (second lprs2set) $ groupWithExtract fst snd ps)
|
|
||||||
where
|
|
||||||
lrrs2set = foldl' f initial
|
|
||||||
where
|
|
||||||
initial = LocalRepoDirectSet False False False
|
|
||||||
f s LocalRepo = s { localRecipRepo = True }
|
|
||||||
f s LocalRepoTeam = s { localRecipRepoTeam = True }
|
|
||||||
f s LocalRepoFollowers = s { localRecipRepoFollowers = True }
|
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
-- Parse URIs into a grouped recipient set
|
|
||||||
-------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
makeRecipientSet
|
|
||||||
:: [LocalActor] -> [LocalPersonCollection] -> LocalRecipientSet
|
|
||||||
makeRecipientSet actors collections =
|
|
||||||
groupLocalRecipients $
|
|
||||||
map groupedRecipientFromActor actors ++
|
|
||||||
map groupedRecipientFromCollection collections
|
|
||||||
|
|
||||||
parseRecipients
|
|
||||||
:: (MonadSite m, SiteEnv m ~ App)
|
|
||||||
=> NonEmpty FedURI
|
|
||||||
-> ExceptT Text m (LocalRecipientSet, [FedURI])
|
|
||||||
parseRecipients recips = do
|
|
||||||
hLocal <- asksSite siteInstanceHost
|
|
||||||
let (locals, remotes) = splitRecipients hLocal recips
|
|
||||||
(lusInvalid, routesInvalid, localsSet) = parseLocalRecipients locals
|
|
||||||
unless (null lusInvalid) $
|
|
||||||
throwE $
|
|
||||||
"Local recipients are invalid routes: " <>
|
|
||||||
T.pack (show $ map (renderObjURI . ObjURI hLocal) lusInvalid)
|
|
||||||
unless (null routesInvalid) $ do
|
|
||||||
renderUrl <- askUrlRender
|
|
||||||
throwE $
|
|
||||||
"Local recipients are non-recipient routes: " <>
|
|
||||||
T.pack (show $ map renderUrl routesInvalid)
|
|
||||||
return (localsSet, remotes)
|
|
||||||
where
|
|
||||||
splitRecipients :: Host -> NonEmpty FedURI -> ([LocalURI], [FedURI])
|
|
||||||
splitRecipients home recips =
|
|
||||||
let (local, remote) = NE.partition ((== home) . objUriAuthority) recips
|
|
||||||
in (map objUriLocal local, remote)
|
|
||||||
|
|
||||||
parseLocalRecipients
|
|
||||||
:: [LocalURI] -> ([LocalURI], [Route App], LocalRecipientSet)
|
|
||||||
parseLocalRecipients lus =
|
|
||||||
let (lusInvalid, routes) = partitionEithers $ map parseRoute lus
|
|
||||||
(routesInvalid, recips) = partitionEithers $ map parseRecip routes
|
|
||||||
(actors, collections) = partitionEithers recips
|
|
||||||
grouped =
|
|
||||||
map groupedRecipientFromActor actors ++
|
|
||||||
map groupedRecipientFromCollection collections
|
|
||||||
in (lusInvalid, routesInvalid, groupLocalRecipients grouped)
|
|
||||||
where
|
|
||||||
parseRoute lu =
|
|
||||||
case decodeRouteLocal lu of
|
|
||||||
Nothing -> Left lu
|
|
||||||
Just route -> Right route
|
|
||||||
parseRecip route =
|
|
||||||
case parseLocalRecipient route of
|
|
||||||
Nothing -> Left route
|
|
||||||
Just recip -> Right recip
|
|
||||||
|
|
||||||
data ParsedAudience u = ParsedAudience
|
|
||||||
{ paudLocalRecips :: LocalRecipientSet
|
|
||||||
, paudRemoteActors :: [(Authority u, NonEmpty LocalURI)]
|
|
||||||
, paudBlinded :: Audience u
|
|
||||||
, paudFwdHosts :: [Authority u]
|
|
||||||
}
|
|
||||||
|
|
||||||
parseAudience
|
|
||||||
:: (MonadSite m, SiteEnv m ~ App)
|
|
||||||
=> Audience URIMode
|
|
||||||
-> ExceptT Text m (Maybe (ParsedAudience URIMode))
|
|
||||||
parseAudience audience = do
|
|
||||||
let recips = concatRecipients audience
|
|
||||||
for (nonEmpty recips) $ \ recipsNE -> do
|
|
||||||
(localsSet, remotes) <- parseRecipients recipsNE
|
|
||||||
let remotesGrouped =
|
|
||||||
groupByHost $ remotes \\ audienceNonActors audience
|
|
||||||
hosts = map fst remotesGrouped
|
|
||||||
return ParsedAudience
|
|
||||||
{ paudLocalRecips = localsSet
|
|
||||||
, paudRemoteActors = remotesGrouped
|
|
||||||
, paudBlinded =
|
|
||||||
audience { audienceBto = [], audienceBcc = [] }
|
|
||||||
, paudFwdHosts =
|
|
||||||
let nonActorHosts =
|
|
||||||
LO.nubSort $
|
|
||||||
map objUriAuthority $ audienceNonActors audience
|
|
||||||
in LO.isect hosts nonActorHosts
|
|
||||||
}
|
|
||||||
where
|
|
||||||
groupByHost :: [FedURI] -> [(Host, NonEmpty LocalURI)]
|
|
||||||
groupByHost = groupAllExtract objUriAuthority objUriLocal
|
|
||||||
|
|
||||||
actorIsMember :: LocalActor -> LocalRecipientSet -> Bool
|
|
||||||
actorIsMember (LocalActorSharer shr) lrSet =
|
|
||||||
case lookup shr lrSet of
|
|
||||||
Just lsrSet -> localRecipSharer $ localRecipSharerDirect lsrSet
|
|
||||||
Nothing -> False
|
|
||||||
actorIsMember (LocalActorProject shr prj) lrSet = fromMaybe False $ do
|
|
||||||
lsrSet <- lookup shr lrSet
|
|
||||||
lprSet <- lookup prj $ localRecipProjectRelated lsrSet
|
|
||||||
return $ localRecipProject $ localRecipProjectDirect $ lprSet
|
|
||||||
actorIsMember (LocalActorRepo shr rp) lrSet = fromMaybe False $ do
|
|
||||||
lsrSet <- lookup shr lrSet
|
|
||||||
lrrSet <- lookup rp $ localRecipRepoRelated lsrSet
|
|
||||||
return $ localRecipRepo $ localRecipRepoDirect $ lrrSet
|
|
||||||
|
|
||||||
actorRecips :: LocalActor -> LocalRecipientSet
|
|
||||||
actorRecips = groupLocalRecipients . (: []) . groupedRecipientFromActor
|
|
||||||
|
|
||||||
localRecipSieve
|
|
||||||
:: LocalRecipientSet -> Bool -> LocalRecipientSet -> LocalRecipientSet
|
|
||||||
localRecipSieve sieve allowActors =
|
|
||||||
localRecipSieve' sieve allowActors allowActors
|
|
||||||
|
|
||||||
localRecipSieve'
|
|
||||||
:: LocalRecipientSet
|
|
||||||
-> Bool
|
|
||||||
-> Bool
|
|
||||||
-> LocalRecipientSet
|
|
||||||
-> LocalRecipientSet
|
|
||||||
localRecipSieve' sieve allowSharers allowOthers =
|
|
||||||
mapMaybe (uncurry applySharerRelated) . sortAlign sieve
|
|
||||||
where
|
|
||||||
onlyActorsJ (LocalProjectRelatedSet (LocalProjectDirectSet j _t _f) _ts) =
|
|
||||||
LocalProjectRelatedSet (LocalProjectDirectSet (j && allowOthers) False False) []
|
|
||||||
onlyActorsR (LocalRepoRelatedSet (LocalRepoDirectSet r _t _f) _ps) =
|
|
||||||
LocalRepoRelatedSet (LocalRepoDirectSet (r && allowOthers) False False) []
|
|
||||||
onlyActorsS (LocalSharerRelatedSet (LocalSharerDirectSet s _f) _ts _ps js rs) =
|
|
||||||
LocalSharerRelatedSet
|
|
||||||
(LocalSharerDirectSet (s && allowSharers) False)
|
|
||||||
[]
|
|
||||||
[]
|
|
||||||
(map (second onlyActorsJ) js)
|
|
||||||
(map (second onlyActorsR) rs)
|
|
||||||
|
|
||||||
applySharerRelated _ (This _) = Nothing
|
|
||||||
applySharerRelated shr (That s) =
|
|
||||||
if allowSharers || allowOthers
|
|
||||||
then Just (shr, onlyActorsS s)
|
|
||||||
else Nothing
|
|
||||||
applySharerRelated shr (These (LocalSharerRelatedSet s' t' p' j' r') (LocalSharerRelatedSet s t p j r)) =
|
|
||||||
Just
|
|
||||||
( shr
|
|
||||||
, LocalSharerRelatedSet
|
|
||||||
(applySharer s' s)
|
|
||||||
(mapMaybe (uncurry applyTicketRelated) $ sortAlign t' t)
|
|
||||||
(mapMaybe (uncurry applyPatchRelated) $ sortAlign p' p)
|
|
||||||
(mapMaybe (uncurry applyProjectRelated) $ sortAlign j' j)
|
|
||||||
(mapMaybe (uncurry applyRepoRelated) $ sortAlign r' r)
|
|
||||||
)
|
|
||||||
where
|
|
||||||
applySharer (LocalSharerDirectSet s' f') (LocalSharerDirectSet s f) =
|
|
||||||
LocalSharerDirectSet (s && (s' || allowSharers)) (f && f')
|
|
||||||
|
|
||||||
applyTicketRelated ltkhid (These t' t) = Just (ltkhid, applyTicket t' t)
|
|
||||||
where
|
|
||||||
applyTicket (LocalTicketDirectSet t' f') (LocalTicketDirectSet t f) =
|
|
||||||
LocalTicketDirectSet (t && t') (f && f')
|
|
||||||
applyTicketRelated _ _ = Nothing
|
|
||||||
|
|
||||||
applyPatchRelated ltkhid (These p' p) = Just (ltkhid, applyPatch p' p)
|
|
||||||
where
|
|
||||||
applyPatch (LocalPatchDirectSet f') (LocalPatchDirectSet f) =
|
|
||||||
LocalPatchDirectSet $ f && f'
|
|
||||||
applyPatchRelated _ _ = Nothing
|
|
||||||
|
|
||||||
applyProjectRelated _ (This _) = Nothing
|
|
||||||
applyProjectRelated prj (That j) =
|
|
||||||
if allowOthers
|
|
||||||
then Just (prj, onlyActorsJ j)
|
|
||||||
else Nothing
|
|
||||||
applyProjectRelated prj (These (LocalProjectRelatedSet j' t') (LocalProjectRelatedSet j t)) =
|
|
||||||
Just
|
|
||||||
( prj
|
|
||||||
, LocalProjectRelatedSet
|
|
||||||
(applyProject j' j)
|
|
||||||
(mapMaybe (uncurry applyTicketRelated) $ sortAlign t' t)
|
|
||||||
)
|
|
||||||
where
|
|
||||||
applyProject (LocalProjectDirectSet j' t' f') (LocalProjectDirectSet j t f) =
|
|
||||||
LocalProjectDirectSet (j && (j' || allowOthers)) (t && t') (f && f')
|
|
||||||
|
|
||||||
applyRepoRelated _ (This _) = Nothing
|
|
||||||
applyRepoRelated rp (That r) =
|
|
||||||
if allowOthers
|
|
||||||
then Just (rp, onlyActorsR r)
|
|
||||||
else Nothing
|
|
||||||
applyRepoRelated rp (These (LocalRepoRelatedSet r' p') (LocalRepoRelatedSet r p)) =
|
|
||||||
Just
|
|
||||||
( rp
|
|
||||||
, LocalRepoRelatedSet
|
|
||||||
(applyRepo r' r)
|
|
||||||
(mapMaybe (uncurry applyPatchRelated) $ sortAlign p' p)
|
|
||||||
)
|
|
||||||
where
|
|
||||||
applyRepo (LocalRepoDirectSet r' t' f') (LocalRepoDirectSet r t f) =
|
|
||||||
LocalRepoDirectSet (r && (r' || allowOthers)) (t && t') (f && f')
|
|
||||||
|
|
||||||
data Aud u
|
|
||||||
= AudLocal [LocalActor] [LocalPersonCollection]
|
|
||||||
| AudRemote (Authority u) [LocalURI] [LocalURI]
|
|
||||||
|
|
||||||
collectAudience
|
|
||||||
:: Foldable f
|
|
||||||
=> f (Aud u)
|
|
||||||
-> ( LocalRecipientSet
|
|
||||||
, [(Authority u, NonEmpty LocalURI)]
|
|
||||||
, [Authority u]
|
|
||||||
, [Route App]
|
|
||||||
, [ObjURI u]
|
|
||||||
)
|
|
||||||
collectAudience auds =
|
|
||||||
let (locals, remotes) = partitionAudience auds
|
|
||||||
(actors, collections) =
|
|
||||||
let organize = LO.nubSort . concat
|
|
||||||
in bimap organize organize $ unzip locals
|
|
||||||
groupedRemotes =
|
|
||||||
let organize = LO.nubSort . sconcat
|
|
||||||
in map (second $ bimap organize organize . NE.unzip) $
|
|
||||||
groupAllExtract fst snd remotes
|
|
||||||
in ( makeRecipientSet actors collections
|
|
||||||
, mapMaybe (\ (h, (as, _)) -> (h,) <$> nonEmpty as) groupedRemotes
|
|
||||||
, [ h | (h, (_, cs)) <- groupedRemotes, not (null cs) ]
|
|
||||||
, map renderLocalActor actors ++
|
|
||||||
map renderLocalPersonCollection collections
|
|
||||||
, concatMap (\ (h, (as, cs)) -> ObjURI h <$> as ++ cs) groupedRemotes
|
|
||||||
)
|
|
||||||
where
|
|
||||||
partitionAudience = foldl' f ([], [])
|
|
||||||
where
|
|
||||||
f (ls, rs) (AudLocal as cs) = ((as, cs) : ls, rs)
|
|
||||||
f (ls, rs) (AudRemote h as cs) = (ls , (h, (as, cs)) : rs)
|
|
399
src/Vervis/Actor.hs
Normal file
399
src/Vervis/Actor.hs
Normal file
|
@ -0,0 +1,399 @@
|
||||||
|
{- This file is part of Vervis.
|
||||||
|
-
|
||||||
|
- Written in 2019, 2020, 2022 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.Actor
|
||||||
|
( getInbox
|
||||||
|
, getOutbox
|
||||||
|
, getOutboxItem
|
||||||
|
, getFollowersCollection
|
||||||
|
, getActorFollowersCollection
|
||||||
|
, getFollowingCollection
|
||||||
|
)
|
||||||
|
where
|
||||||
|
|
||||||
|
import Control.Applicative ((<|>))
|
||||||
|
import Control.Concurrent.STM.TVar (readTVarIO, modifyTVar')
|
||||||
|
import Control.Exception hiding (Handler)
|
||||||
|
import Control.Monad
|
||||||
|
import Control.Monad.IO.Class (liftIO)
|
||||||
|
import Control.Monad.Logger.CallStack
|
||||||
|
import Control.Monad.STM (atomically)
|
||||||
|
import Control.Monad.Trans.Except
|
||||||
|
import Control.Monad.Trans.Reader
|
||||||
|
import Data.Aeson
|
||||||
|
import Data.Aeson.Encode.Pretty
|
||||||
|
import Data.Bifunctor
|
||||||
|
import Data.Bitraversable
|
||||||
|
import Data.Foldable (for_)
|
||||||
|
import Data.List
|
||||||
|
import Data.Maybe
|
||||||
|
import Data.Text (Text)
|
||||||
|
import Data.Text.Lazy.Encoding (decodeUtf8)
|
||||||
|
import Data.Time.Clock
|
||||||
|
import Data.Time.Interval (TimeInterval, toTimeUnit)
|
||||||
|
import Data.Time.Units (Second)
|
||||||
|
import Data.Traversable
|
||||||
|
import Database.Persist
|
||||||
|
import Database.Persist.Sql
|
||||||
|
import Network.HTTP.Types.Status
|
||||||
|
import Text.Blaze.Html (Html, preEscapedToHtml)
|
||||||
|
import Text.Blaze.Html.Renderer.Text
|
||||||
|
import Text.HTML.SanitizeXSS
|
||||||
|
import Text.Shakespeare.I18N (RenderMessage)
|
||||||
|
import Yesod.Core hiding (logDebug)
|
||||||
|
import Yesod.Core.Handler
|
||||||
|
import Yesod.Form.Fields
|
||||||
|
import Yesod.Form.Functions
|
||||||
|
import Yesod.Form.Types
|
||||||
|
import Yesod.Persist.Core
|
||||||
|
|
||||||
|
import qualified Data.ByteString.Char8 as BC (unpack)
|
||||||
|
import qualified Data.HashMap.Strict as M
|
||||||
|
import qualified Data.Text as T
|
||||||
|
import qualified Data.Text.Lazy as TL (toStrict)
|
||||||
|
import qualified Data.Vector as V
|
||||||
|
import qualified Database.Esqueleto as E
|
||||||
|
|
||||||
|
import Database.Persist.JSON
|
||||||
|
import Network.FedURI
|
||||||
|
import Web.ActivityPub hiding (Project (..), ActorLocal (..))
|
||||||
|
import Yesod.ActivityPub
|
||||||
|
import Yesod.Auth.Unverified
|
||||||
|
import Yesod.FedURI
|
||||||
|
import Yesod.Hashids
|
||||||
|
import Yesod.MonadSite
|
||||||
|
import Yesod.RenderSource
|
||||||
|
|
||||||
|
import Data.Aeson.Local
|
||||||
|
import Data.Either.Local
|
||||||
|
import Data.EventTime.Local
|
||||||
|
import Data.Paginate.Local
|
||||||
|
import Data.Time.Clock.Local
|
||||||
|
import Database.Persist.Local
|
||||||
|
import Yesod.Persist.Local
|
||||||
|
|
||||||
|
import Vervis.ActivityPub
|
||||||
|
import Vervis.ActorKey
|
||||||
|
import Vervis.API
|
||||||
|
import Vervis.FedURI
|
||||||
|
import Vervis.Federation
|
||||||
|
import Vervis.Federation.Auth
|
||||||
|
import Vervis.Foundation
|
||||||
|
import Vervis.Model hiding (Ticket)
|
||||||
|
import Vervis.Model.Ident
|
||||||
|
import Vervis.Paginate
|
||||||
|
import Vervis.Recipient
|
||||||
|
import Vervis.Settings
|
||||||
|
import Vervis.Ticket
|
||||||
|
|
||||||
|
getShowTime = showTime <$> liftIO getCurrentTime
|
||||||
|
where
|
||||||
|
showTime now =
|
||||||
|
showEventTime .
|
||||||
|
intervalToEventTime .
|
||||||
|
FriendlyConvert .
|
||||||
|
diffUTCTime now
|
||||||
|
|
||||||
|
objectSummary o =
|
||||||
|
case M.lookup "summary" o of
|
||||||
|
Just (String t) | not (T.null t) -> Just t
|
||||||
|
_ -> Nothing
|
||||||
|
|
||||||
|
objectId o =
|
||||||
|
case M.lookup "id" o <|> M.lookup "@id" o of
|
||||||
|
Just (String t) | not (T.null t) -> t
|
||||||
|
_ -> error "'id' field not found"
|
||||||
|
|
||||||
|
getInbox here actor hash = do
|
||||||
|
key <- decodeKeyHashid404 hash
|
||||||
|
(total, pages, mpage) <- runDB $ do
|
||||||
|
inboxID <- do
|
||||||
|
actorID <- actor <$> get404 key
|
||||||
|
actorInbox <$> getJust actorID
|
||||||
|
getPageAndNavCount
|
||||||
|
(countItems inboxID)
|
||||||
|
(\ off lim -> map adaptItem <$> getItems inboxID off lim)
|
||||||
|
|
||||||
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
|
encodeRoutePageLocal <- getEncodeRoutePageLocal
|
||||||
|
let here' = here hash
|
||||||
|
pageUrl = encodeRoutePageLocal here'
|
||||||
|
host <- getsYesod $ appInstanceHost . appSettings
|
||||||
|
selectRep $
|
||||||
|
case mpage of
|
||||||
|
Nothing -> do
|
||||||
|
provideAP $ pure $ Doc host $ Collection
|
||||||
|
{ collectionId = encodeRouteLocal here'
|
||||||
|
, collectionType = CollectionTypeOrdered
|
||||||
|
, collectionTotalItems = Just total
|
||||||
|
, collectionCurrent = Nothing
|
||||||
|
, collectionFirst = Just $ pageUrl 1
|
||||||
|
, collectionLast = Just $ pageUrl pages
|
||||||
|
, collectionItems = [] :: [Text]
|
||||||
|
}
|
||||||
|
provideRep (redirectFirstPage here' :: Handler Html)
|
||||||
|
Just (items, navModel) -> do
|
||||||
|
let current = nmCurrent navModel
|
||||||
|
provideAP $ pure $ Doc host $ 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 fst items
|
||||||
|
}
|
||||||
|
provideRep $ do
|
||||||
|
let pageNav = navWidget navModel
|
||||||
|
showTime <- getShowTime
|
||||||
|
defaultLayout $(widgetFile "person/inbox")
|
||||||
|
where
|
||||||
|
countItems ibid =
|
||||||
|
(+) <$> count [InboxItemLocalInbox ==. ibid]
|
||||||
|
<*> count [InboxItemRemoteInbox ==. ibid]
|
||||||
|
getItems ibid off lim =
|
||||||
|
E.select $ E.from $
|
||||||
|
\ (ib `E.LeftOuterJoin` (ibl `E.InnerJoin` ob) `E.LeftOuterJoin` (ibr `E.InnerJoin` ract)) -> do
|
||||||
|
E.on $ ibr E.?. InboxItemRemoteActivity E.==. ract E.?. RemoteActivityId
|
||||||
|
E.on $ E.just (ib E.^. InboxItemId) E.==. ibr E.?. InboxItemRemoteItem
|
||||||
|
E.on $ ibl E.?. InboxItemLocalActivity E.==. ob E.?. OutboxItemId
|
||||||
|
E.on $ E.just (ib E.^. InboxItemId) E.==. ibl E.?. InboxItemLocalItem
|
||||||
|
E.where_
|
||||||
|
$ ( E.isNothing (ibr E.?. InboxItemRemoteInbox) E.||.
|
||||||
|
ibr E.?. InboxItemRemoteInbox E.==. E.just (E.val ibid)
|
||||||
|
)
|
||||||
|
E.&&.
|
||||||
|
( E.isNothing (ibl E.?. InboxItemLocalInbox) E.||.
|
||||||
|
ibl E.?. InboxItemLocalInbox E.==. E.just (E.val ibid)
|
||||||
|
)
|
||||||
|
E.orderBy [E.desc $ ib E.^. InboxItemId]
|
||||||
|
E.offset $ fromIntegral off
|
||||||
|
E.limit $ fromIntegral lim
|
||||||
|
return
|
||||||
|
( ib E.^. InboxItemId
|
||||||
|
, ob E.?. OutboxItemActivity
|
||||||
|
, ob E.?. OutboxItemPublished
|
||||||
|
, ract E.?. RemoteActivityContent
|
||||||
|
, ract E.?. RemoteActivityReceived
|
||||||
|
)
|
||||||
|
adaptItem
|
||||||
|
(E.Value ibid, E.Value mact, E.Value mpub, E.Value mobj, E.Value mrec) =
|
||||||
|
case (mact, mpub, mobj, mrec) of
|
||||||
|
(Nothing, Nothing, Nothing, Nothing) ->
|
||||||
|
error $ ibiidString ++ " neither local nor remote"
|
||||||
|
(Just _, Just _, Just _, Just _) ->
|
||||||
|
error $ ibiidString ++ " both local and remote"
|
||||||
|
(Just act, Just pub, Nothing, Nothing) ->
|
||||||
|
(persistJSONObject act, (pub, False))
|
||||||
|
(Nothing, Nothing, Just obj, Just rec) ->
|
||||||
|
(persistJSONObject obj, (rec, True))
|
||||||
|
_ -> error $ "Unexpected query result for " ++ ibiidString
|
||||||
|
where
|
||||||
|
ibiidString = "InboxItem #" ++ show (fromSqlKey ibid)
|
||||||
|
|
||||||
|
getOutbox here actor hash = do
|
||||||
|
key <- decodeKeyHashid404 hash
|
||||||
|
(total, pages, mpage) <- runDB $ do
|
||||||
|
outboxID <- do
|
||||||
|
actorID <- actor <$> get404 key
|
||||||
|
actorOutbox <$> getJust actorID
|
||||||
|
let countAllItems = count [OutboxItemOutbox ==. outboxID]
|
||||||
|
selectItems off lim = selectList [OutboxItemOutbox ==. outboxID] [Desc OutboxItemId, OffsetBy off, LimitTo lim]
|
||||||
|
getPageAndNavCount countAllItems selectItems
|
||||||
|
|
||||||
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
|
encodeRoutePageLocal <- getEncodeRoutePageLocal
|
||||||
|
let here' = here hash
|
||||||
|
pageUrl = encodeRoutePageLocal here'
|
||||||
|
host <- getsYesod $ appInstanceHost . appSettings
|
||||||
|
selectRep $
|
||||||
|
case mpage of
|
||||||
|
Nothing -> do
|
||||||
|
provideAP $ pure $ Doc host $ Collection
|
||||||
|
{ collectionId = encodeRouteLocal here'
|
||||||
|
, collectionType = CollectionTypeOrdered
|
||||||
|
, collectionTotalItems = Just total
|
||||||
|
, collectionCurrent = Nothing
|
||||||
|
, collectionFirst = Just $ pageUrl 1
|
||||||
|
, collectionLast = Just $ pageUrl pages
|
||||||
|
, collectionItems = [] :: [Text]
|
||||||
|
}
|
||||||
|
provideRep (redirectFirstPage here' :: Handler Html)
|
||||||
|
Just (items, navModel) -> do
|
||||||
|
let current = nmCurrent navModel
|
||||||
|
provideAP $ pure $ Doc host $ 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 (persistJSONObject . outboxItemActivity . entityVal) items
|
||||||
|
}
|
||||||
|
provideRep $ do
|
||||||
|
let pageNav = navWidget navModel
|
||||||
|
showTime <- getShowTime
|
||||||
|
defaultLayout $(widgetFile "person/outbox")
|
||||||
|
|
||||||
|
getOutboxItem here actor topicHash itemHash = do
|
||||||
|
topicID <- decodeKeyHashid404 topicHash
|
||||||
|
itemID <- decodeKeyHashid404 itemHash
|
||||||
|
body <- runDB $ do
|
||||||
|
outboxID <- do
|
||||||
|
actorID <- actor <$> get404 topicID
|
||||||
|
actorOutbox <$> getJust actorID
|
||||||
|
item <- get404 itemID
|
||||||
|
unless (outboxItemOutbox item == outboxID) notFound
|
||||||
|
return $ outboxItemActivity item
|
||||||
|
let here' = here topicHash itemHash
|
||||||
|
provideHtmlAndAP'' body $ redirectToPrettyJSON here'
|
||||||
|
|
||||||
|
getLocalActors
|
||||||
|
:: [ActorId] -> ReaderT SqlBackend Handler [LocalActorBy Key]
|
||||||
|
getLocalActors actorIDs = do
|
||||||
|
localActors <-
|
||||||
|
concat <$> sequenceA
|
||||||
|
[ map LocalActorPerson <$>
|
||||||
|
selectKeysList [PersonActor <-. actorIDs] []
|
||||||
|
, map LocalActorGroup <$>
|
||||||
|
selectKeysList [GroupActor <-. actorIDs] []
|
||||||
|
, map LocalActorRepo <$>
|
||||||
|
selectKeysList [RepoActor <-. actorIDs] []
|
||||||
|
, map LocalActorDeck <$>
|
||||||
|
selectKeysList [DeckActor <-. actorIDs] []
|
||||||
|
, map LocalActorLoom <$>
|
||||||
|
selectKeysList [LoomActor <-. actorIDs] []
|
||||||
|
]
|
||||||
|
case compare (length localActors) (length actorIDs) of
|
||||||
|
LT -> error "Found actor ID not used by any specific actor"
|
||||||
|
GT -> error "Found actor ID used by multiple specific actors"
|
||||||
|
EQ -> return localActors
|
||||||
|
|
||||||
|
getFollowersCollection
|
||||||
|
:: Route App -> AppDB FollowerSetId -> Handler TypedContent
|
||||||
|
getFollowersCollection here getFsid = do
|
||||||
|
(locals, remotes, l, r) <- runDB $ do
|
||||||
|
fsid <- getFsid
|
||||||
|
(,,,) <$> do actorIDs <-
|
||||||
|
map (followActor . entityVal) <$>
|
||||||
|
selectList
|
||||||
|
[FollowTarget ==. fsid, FollowPublic ==. True]
|
||||||
|
[]
|
||||||
|
getLocalActors actorIDs
|
||||||
|
<*> do E.select $ E.from $ \ (rf `E.InnerJoin` ra `E.InnerJoin` ro `E.InnerJoin` i) -> do
|
||||||
|
E.on $ ro E.^. RemoteObjectInstance E.==. i E.^. InstanceId
|
||||||
|
E.on $ ra E.^. RemoteActorIdent E.==. ro E.^. RemoteObjectId
|
||||||
|
E.on $ rf E.^. RemoteFollowActor E.==. ra E.^. RemoteActorId
|
||||||
|
E.where_
|
||||||
|
$ rf E.^. RemoteFollowTarget E.==. E.val fsid
|
||||||
|
E.&&. rf E.^. RemoteFollowPublic E.==. E.val True
|
||||||
|
return
|
||||||
|
( i E.^. InstanceHost
|
||||||
|
, ro E.^. RemoteObjectIdent
|
||||||
|
)
|
||||||
|
<*> count [FollowTarget ==. fsid]
|
||||||
|
<*> count [RemoteFollowTarget ==. fsid]
|
||||||
|
|
||||||
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
|
encodeRouteHome <- getEncodeRouteHome
|
||||||
|
hashActor <- getHashLocalActor
|
||||||
|
let followersAP = Collection
|
||||||
|
{ collectionId = encodeRouteLocal here
|
||||||
|
, collectionType = CollectionTypeUnordered
|
||||||
|
, collectionTotalItems = Just $ l + r
|
||||||
|
, collectionCurrent = Nothing
|
||||||
|
, collectionFirst = Nothing
|
||||||
|
, collectionLast = Nothing
|
||||||
|
, collectionItems =
|
||||||
|
map (encodeRouteHome . renderLocalActor . hashActor) locals ++
|
||||||
|
map (uncurry ObjURI . bimap E.unValue E.unValue) remotes
|
||||||
|
}
|
||||||
|
provideHtmlAndAP followersAP $ redirectToPrettyJSON here
|
||||||
|
|
||||||
|
getActorFollowersCollection here actor hash = do
|
||||||
|
key <- decodeKeyHashid404 hash
|
||||||
|
getFollowersCollection (here hash) (getFsid key)
|
||||||
|
where
|
||||||
|
getFsid key = do
|
||||||
|
actorID <- actor <$> get404 key
|
||||||
|
actorFollowers <$> getJust actorID
|
||||||
|
|
||||||
|
getFollowingCollection here actor hash = do
|
||||||
|
key <- decodeKeyHashid404 hash
|
||||||
|
(localTotal, localActors, workItems, remotes) <- runDB $ do
|
||||||
|
followerActorID <- actor <$> get404 key
|
||||||
|
followerSetIDs <-
|
||||||
|
map (followTarget . entityVal) <$>
|
||||||
|
selectList [FollowActor ==. followerActorID] []
|
||||||
|
actorIDs <- selectKeysList [ActorFollowers <-. followerSetIDs] []
|
||||||
|
ticketIDs <- selectKeysList [TicketFollowers <-. followerSetIDs] []
|
||||||
|
(,,,) (length followerSetIDs)
|
||||||
|
<$> getLocalActors actorIDs
|
||||||
|
<*> ((++) <$> getTickets ticketIDs <*> getCloths ticketIDs)
|
||||||
|
<*> getRemotes followerActorID
|
||||||
|
|
||||||
|
hashActor <- getHashLocalActor
|
||||||
|
workItemRoute <- askWorkItemRoute
|
||||||
|
let locals =
|
||||||
|
map (renderLocalActor . hashActor) localActors ++
|
||||||
|
map workItemRoute workItems
|
||||||
|
unless (length locals == localTotal) $
|
||||||
|
error "Bug! List length mismatch"
|
||||||
|
|
||||||
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
|
encodeRouteHome <- getEncodeRouteHome
|
||||||
|
let here' = here hash
|
||||||
|
followingAP = Collection
|
||||||
|
{ collectionId = encodeRouteLocal here'
|
||||||
|
, collectionType = CollectionTypeUnordered
|
||||||
|
, collectionTotalItems = Just $ localTotal + length remotes
|
||||||
|
, collectionCurrent = Nothing
|
||||||
|
, collectionFirst = Nothing
|
||||||
|
, collectionLast = Nothing
|
||||||
|
, collectionItems = map encodeRouteHome locals ++ remotes
|
||||||
|
}
|
||||||
|
provideHtmlAndAP followingAP $ redirectToPrettyJSON here'
|
||||||
|
where
|
||||||
|
getTickets tids =
|
||||||
|
map workItem <$> selectList [TicketDeckTicket <-. tids] []
|
||||||
|
where
|
||||||
|
workItem (Entity t (TicketDeck _ d)) = WorkItemTicket d t
|
||||||
|
getCloths tids =
|
||||||
|
map workItem <$> selectList [TicketLoomTicket <-. tids] []
|
||||||
|
where
|
||||||
|
workItem (Entity c (TicketLoom _ l _)) = WorkItemCloth l c
|
||||||
|
getRemotes aid =
|
||||||
|
map (followRemoteTarget . entityVal) <$>
|
||||||
|
selectList [FollowRemoteActor ==. aid] []
|
|
@ -1,6 +1,7 @@
|
||||||
{- This file is part of Vervis.
|
{- This file is part of Vervis.
|
||||||
-
|
-
|
||||||
- Written in 2016, 2018, 2019, 2020 by fr33domlover <fr33domlover@riseup.net>.
|
- Written in 2016, 2018, 2019, 2020, 2022
|
||||||
|
- 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.
|
||||||
-
|
-
|
||||||
|
@ -84,6 +85,7 @@ import Yesod.Mail.Send (runMailer)
|
||||||
import Control.Concurrent.ResultShare
|
import Control.Concurrent.ResultShare
|
||||||
import Data.KeyFile
|
import Data.KeyFile
|
||||||
import Network.FedURI
|
import Network.FedURI
|
||||||
|
import Yesod.Hashids
|
||||||
import Yesod.MonadSite
|
import Yesod.MonadSite
|
||||||
|
|
||||||
import Control.Concurrent.Local
|
import Control.Concurrent.Local
|
||||||
|
@ -103,20 +105,19 @@ import Vervis.RemoteActorStore
|
||||||
-- Don't forget to add new modules to your cabal file!
|
-- Don't forget to add new modules to your cabal file!
|
||||||
import Vervis.Handler.Client
|
import Vervis.Handler.Client
|
||||||
import Vervis.Handler.Common
|
import Vervis.Handler.Common
|
||||||
import Vervis.Handler.Git
|
import Vervis.Handler.Cloth
|
||||||
|
import Vervis.Handler.Deck
|
||||||
|
--import Vervis.Handler.Git
|
||||||
import Vervis.Handler.Group
|
import Vervis.Handler.Group
|
||||||
import Vervis.Handler.Home
|
--import Vervis.Handler.Key
|
||||||
import Vervis.Handler.Inbox
|
import Vervis.Handler.Loom
|
||||||
import Vervis.Handler.Key
|
|
||||||
import Vervis.Handler.Patch
|
|
||||||
import Vervis.Handler.Person
|
import Vervis.Handler.Person
|
||||||
import Vervis.Handler.Project
|
|
||||||
import Vervis.Handler.Repo
|
import Vervis.Handler.Repo
|
||||||
import Vervis.Handler.Role
|
--import Vervis.Handler.Role
|
||||||
import Vervis.Handler.Sharer
|
--import Vervis.Handler.Sharer
|
||||||
import Vervis.Handler.Ticket
|
import Vervis.Handler.Ticket
|
||||||
import Vervis.Handler.Wiki
|
--import Vervis.Handler.Wiki
|
||||||
import Vervis.Handler.Workflow
|
--import Vervis.Handler.Workflow
|
||||||
|
|
||||||
import Vervis.Migration (migrateDB)
|
import Vervis.Migration (migrateDB)
|
||||||
import Vervis.Model
|
import Vervis.Model
|
||||||
|
@ -230,8 +231,8 @@ makeFoundation appSettings = do
|
||||||
return app
|
return app
|
||||||
where
|
where
|
||||||
verifyRepoDir = do
|
verifyRepoDir = do
|
||||||
repos <- lift repoTreeFromDir
|
repos <- lift reposFromDir
|
||||||
repos' <- repoTreeFromDB
|
repos' <- reposFromDB
|
||||||
unless (repos == repos') $ liftIO $ do
|
unless (repos == repos') $ liftIO $ do
|
||||||
putStrLn "Repo tree based on filesystem:"
|
putStrLn "Repo tree based on filesystem:"
|
||||||
printRepos repos
|
printRepos repos
|
||||||
|
@ -240,31 +241,23 @@ makeFoundation appSettings = do
|
||||||
throwIO $ userError "Repo dir check failed!"
|
throwIO $ userError "Repo dir check failed!"
|
||||||
liftIO $ printRepos repos
|
liftIO $ printRepos repos
|
||||||
where
|
where
|
||||||
printRepos = traverse_ $ \ (shr, rps) ->
|
printRepos = traverse_ $ \ (rp, vcs) ->
|
||||||
for_ rps $ \ (rp, vcs) ->
|
putStrLn $
|
||||||
putStrLn $
|
"Found repo " ++ rp ++
|
||||||
"Found repo " ++
|
" [" ++ T.unpack (versionControlSystemName vcs) ++ "]"
|
||||||
shr ++ " / " ++ rp ++
|
reposFromDir = do
|
||||||
" [" ++ T.unpack (versionControlSystemName vcs) ++ "]"
|
|
||||||
repoTreeFromDir = do
|
|
||||||
dir <- askRepoRootDir
|
dir <- askRepoRootDir
|
||||||
outers <- liftIO $ sort <$> listDirectory dir
|
subdirs <- liftIO $ sort <$> listDirectory dir
|
||||||
repos <- for outers $ \ outer -> do
|
for subdirs $ \ subdir -> do
|
||||||
let path = dir </> outer
|
checkDir $ dir </> subdir
|
||||||
checkDir path
|
vcs <- do
|
||||||
inners <- liftIO $ sort <$> listDirectory path
|
mvcs <- detectVcs $ dir </> subdir
|
||||||
inners' <- for inners $ \ inner -> do
|
let ref = dir ++ "/" ++ subdir
|
||||||
checkDir $ path </> inner
|
case mvcs of
|
||||||
vcs <- do
|
Left False -> error $ "Failed to detect VCS: " ++ ref
|
||||||
mvcs <- detectVcs $ path </> inner
|
Left True -> error $ "Detected both VCSs: " ++ ref
|
||||||
let ref = outer ++ "/" ++ inner
|
Right v -> return v
|
||||||
case mvcs of
|
return (subdir, vcs)
|
||||||
Left False -> error $ "Failed to detect VCS: " ++ ref
|
|
||||||
Left True -> error $ "Detected both VCSs: " ++ ref
|
|
||||||
Right v -> return v
|
|
||||||
return (inner, vcs)
|
|
||||||
return $ (outer,) <$> nonEmpty inners'
|
|
||||||
return $ catMaybes repos
|
|
||||||
where
|
where
|
||||||
checkDir path = liftIO $ do
|
checkDir path = liftIO $ do
|
||||||
isdir <- doesDirectoryExist path
|
isdir <- doesDirectoryExist path
|
||||||
|
@ -280,18 +273,12 @@ makeFoundation appSettings = do
|
||||||
(False, True) -> Right VCSGit
|
(False, True) -> Right VCSGit
|
||||||
(False, False) -> Left False
|
(False, False) -> Left False
|
||||||
(True, True) -> Left True
|
(True, True) -> Left True
|
||||||
repoTreeFromDB =
|
reposFromDB = do
|
||||||
fmap adapt $ E.select $ E.from $ \ (s `E.InnerJoin` r) -> do
|
hashRepo <- getEncodeKeyHashid
|
||||||
E.on $ s E.^. SharerId E.==. r E.^. RepoSharer
|
sortOn fst . map (adapt hashRepo) <$> selectList [] []
|
||||||
E.orderBy [E.asc $ s E.^. SharerIdent, E.asc $ r E.^. RepoIdent]
|
|
||||||
return (s E.^. SharerIdent, (r E.^. RepoIdent, r E.^. RepoVcs))
|
|
||||||
where
|
where
|
||||||
adapt =
|
adapt hashRepo (Entity repoID repo) =
|
||||||
groupWithExtract
|
(T.unpack $ keyHashidText $ hashRepo repoID, repoVcs repo)
|
||||||
(lower . unShrIdent . E.unValue . fst)
|
|
||||||
(first (lower . unRpIdent) . bimap E.unValue E.unValue . snd)
|
|
||||||
where
|
|
||||||
lower = T.unpack . CI.foldedCase
|
|
||||||
migrate :: MonadLogger m => Text -> ReaderT b m (Either Text (Int, Int)) -> ReaderT b m ()
|
migrate :: MonadLogger m => Text -> ReaderT b m (Either Text (Int, Int)) -> ReaderT b m ()
|
||||||
migrate name a = do
|
migrate name a = do
|
||||||
r <- a
|
r <- a
|
||||||
|
@ -372,6 +359,7 @@ sshServer :: App -> IO ()
|
||||||
sshServer foundation =
|
sshServer foundation =
|
||||||
runSsh
|
runSsh
|
||||||
(appSettings foundation)
|
(appSettings foundation)
|
||||||
|
(appHashidsContext foundation)
|
||||||
(appConnPool foundation)
|
(appConnPool foundation)
|
||||||
(loggingFunction foundation)
|
(loggingFunction foundation)
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{- This file is part of Vervis.
|
{- This file is part of Vervis.
|
||||||
-
|
-
|
||||||
- Written in 2018, 2020 by fr33domlover <fr33domlover@riseup.net>.
|
- Written in 2018, 2020, 2022 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.
|
||||||
-
|
-
|
||||||
|
@ -25,14 +25,16 @@ import Yesod.Feed
|
||||||
|
|
||||||
import qualified Data.Text as T (concat)
|
import qualified Data.Text as T (concat)
|
||||||
|
|
||||||
|
import Yesod.Hashids
|
||||||
|
|
||||||
import Vervis.Changes
|
import Vervis.Changes
|
||||||
import Vervis.Foundation
|
import Vervis.Foundation
|
||||||
import Vervis.Model.Ident
|
import Vervis.Model
|
||||||
import Development.PatchMediaType
|
import Development.PatchMediaType
|
||||||
|
|
||||||
changeEntry :: ShrIdent -> RpIdent -> LogEntry -> FeedEntry (Route App)
|
changeEntry :: KeyHashid Repo -> LogEntry -> FeedEntry (Route App)
|
||||||
changeEntry shr rp le = FeedEntry
|
changeEntry rp le = FeedEntry
|
||||||
{ feedEntryLink = RepoCommitR shr rp $ leHash le
|
{ feedEntryLink = RepoCommitR rp $ leHash le
|
||||||
, feedEntryUpdated = fst $ leTime le
|
, feedEntryUpdated = fst $ leTime le
|
||||||
, feedEntryTitle = leMessage le
|
, feedEntryTitle = leMessage le
|
||||||
, feedEntryContent = mempty
|
, feedEntryContent = mempty
|
||||||
|
@ -40,15 +42,14 @@ changeEntry shr rp le = FeedEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
changeFeed
|
changeFeed
|
||||||
:: ShrIdent -- ^ Sharer name
|
:: KeyHashid Repo -- ^ Repo key
|
||||||
-> RpIdent -- ^ Repo name
|
|
||||||
-> Maybe Text -- ^ Optional branch name
|
-> Maybe Text -- ^ Optional branch name
|
||||||
-> VersionControlSystem -- ^ To pick VCS specific terms
|
-> VersionControlSystem -- ^ To pick VCS specific terms
|
||||||
-> [LogEntry] -- ^ Changes, recent first
|
-> [LogEntry] -- ^ Changes, recent first
|
||||||
-> Feed (Route App)
|
-> Feed (Route App)
|
||||||
changeFeed shr repo mbranch vcs les = Feed
|
changeFeed repo mbranch vcs les = Feed
|
||||||
{ feedTitle = T.concat
|
{ feedTitle = T.concat
|
||||||
[ rp2text repo
|
[ keyHashidText repo
|
||||||
, case mbranch of
|
, case mbranch of
|
||||||
Nothing -> ""
|
Nothing -> ""
|
||||||
Just b -> ":" <> b
|
Just b -> ":" <> b
|
||||||
|
@ -59,16 +60,16 @@ changeFeed shr repo mbranch vcs les = Feed
|
||||||
]
|
]
|
||||||
, feedLinkSelf =
|
, feedLinkSelf =
|
||||||
case mbranch of
|
case mbranch of
|
||||||
Nothing -> RepoHeadChangesR shr repo
|
Nothing -> RepoCommitsR repo
|
||||||
Just b -> RepoChangesR shr repo b
|
Just b -> RepoBranchCommitsR repo b
|
||||||
, feedLinkHome =
|
, feedLinkHome =
|
||||||
case mbranch of
|
case mbranch of
|
||||||
Nothing -> RepoHeadChangesR shr repo
|
Nothing -> RepoCommitsR repo
|
||||||
Just b -> RepoChangesR shr repo b
|
Just b -> RepoBranchCommitsR repo b
|
||||||
, feedAuthor = shr2text shr
|
, feedAuthor = keyHashidText repo
|
||||||
, feedDescription = mempty
|
, feedDescription = mempty
|
||||||
, feedLanguage = "en"
|
, feedLanguage = "en"
|
||||||
, feedUpdated = fst $ leTime $ head les
|
, feedUpdated = fst $ leTime $ head les
|
||||||
, feedLogo = Nothing
|
, feedLogo = Nothing
|
||||||
, feedEntries = map (changeEntry shr repo) les
|
, feedEntries = map (changeEntry repo) les
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,14 +22,12 @@ module Vervis.Client
|
||||||
, followTicket
|
, followTicket
|
||||||
, followRepo
|
, followRepo
|
||||||
, offerTicket
|
, offerTicket
|
||||||
, createTicket
|
|
||||||
, resolve
|
, resolve
|
||||||
, undoFollowSharer
|
, undoFollowSharer
|
||||||
, undoFollowProject
|
, undoFollowProject
|
||||||
, undoFollowTicket
|
, undoFollowTicket
|
||||||
, undoFollowRepo
|
, undoFollowRepo
|
||||||
, unresolve
|
, unresolve
|
||||||
, createMR
|
|
||||||
, offerMR
|
, offerMR
|
||||||
, createDeck
|
, createDeck
|
||||||
)
|
)
|
||||||
|
@ -69,11 +67,11 @@ import Data.Either.Local
|
||||||
import Database.Persist.Local
|
import Database.Persist.Local
|
||||||
|
|
||||||
import Vervis.ActivityPub
|
import Vervis.ActivityPub
|
||||||
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.Recipient
|
||||||
import Vervis.Ticket
|
import Vervis.Ticket
|
||||||
import Vervis.WorkItem
|
import Vervis.WorkItem
|
||||||
|
|
||||||
|
@ -87,6 +85,8 @@ createThread
|
||||||
-> Route App
|
-> Route App
|
||||||
-> m (Either Text (Note URIMode))
|
-> m (Either Text (Note URIMode))
|
||||||
createThread shrAuthor (TextPandocMarkdown msg) hDest recipsA recipsC context = runExceptT $ do
|
createThread shrAuthor (TextPandocMarkdown msg) hDest recipsA recipsC context = runExceptT $ do
|
||||||
|
error "Temporarily disabled"
|
||||||
|
{-
|
||||||
encodeRouteLocal <- getEncodeRouteLocal
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
let encodeRecipRoute = ObjURI hDest . encodeRouteLocal
|
let encodeRecipRoute = ObjURI hDest . encodeRouteLocal
|
||||||
contentHtml <- ExceptT . pure $ renderPandocMarkdown msg
|
contentHtml <- ExceptT . pure $ renderPandocMarkdown msg
|
||||||
|
@ -109,6 +109,7 @@ createThread shrAuthor (TextPandocMarkdown msg) hDest recipsA recipsC context =
|
||||||
, noteSource = msg
|
, noteSource = msg
|
||||||
, noteContent = contentHtml
|
, noteContent = contentHtml
|
||||||
}
|
}
|
||||||
|
-}
|
||||||
|
|
||||||
createReply
|
createReply
|
||||||
:: ShrIdent
|
:: ShrIdent
|
||||||
|
@ -120,6 +121,8 @@ createReply
|
||||||
-> MessageId
|
-> MessageId
|
||||||
-> Handler (Either Text (Note URIMode))
|
-> Handler (Either Text (Note URIMode))
|
||||||
createReply shrAuthor (TextPandocMarkdown msg) hDest recipsA recipsC context midParent = runExceptT $ do
|
createReply shrAuthor (TextPandocMarkdown msg) hDest recipsA recipsC context midParent = runExceptT $ do
|
||||||
|
error "Temporarily disabled"
|
||||||
|
{-
|
||||||
encodeRouteHome <- getEncodeRouteHome
|
encodeRouteHome <- getEncodeRouteHome
|
||||||
encodeRouteLocal <- getEncodeRouteLocal
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
let encodeRecipRoute = ObjURI hDest . encodeRouteLocal
|
let encodeRecipRoute = ObjURI hDest . encodeRouteLocal
|
||||||
|
@ -159,11 +162,14 @@ createReply shrAuthor (TextPandocMarkdown msg) hDest recipsA recipsC context mid
|
||||||
, noteSource = msg
|
, noteSource = msg
|
||||||
, noteContent = contentHtml
|
, noteContent = contentHtml
|
||||||
}
|
}
|
||||||
|
-}
|
||||||
|
|
||||||
follow
|
follow
|
||||||
:: (MonadHandler m, HandlerSite m ~ App)
|
:: (MonadHandler m, HandlerSite m ~ App)
|
||||||
=> ShrIdent -> ObjURI URIMode -> ObjURI URIMode -> Bool -> m (TextHtml, Audience URIMode, AP.Follow URIMode)
|
=> ShrIdent -> ObjURI URIMode -> ObjURI URIMode -> Bool -> m (TextHtml, Audience URIMode, AP.Follow URIMode)
|
||||||
follow shrAuthor uObject@(ObjURI hObject luObject) uRecip hide = do
|
follow shrAuthor uObject@(ObjURI hObject luObject) uRecip hide = do
|
||||||
|
error "Temporarily disabled"
|
||||||
|
{-
|
||||||
summary <-
|
summary <-
|
||||||
TextHtml . TL.toStrict . renderHtml <$>
|
TextHtml . TL.toStrict . renderHtml <$>
|
||||||
withUrlRenderer
|
withUrlRenderer
|
||||||
|
@ -186,44 +192,59 @@ follow shrAuthor uObject@(ObjURI hObject luObject) uRecip hide = do
|
||||||
}
|
}
|
||||||
audience = Audience [uRecip] [] [] [] [] []
|
audience = Audience [uRecip] [] [] [] [] []
|
||||||
return (summary, audience, followAP)
|
return (summary, audience, followAP)
|
||||||
|
-}
|
||||||
|
|
||||||
followSharer
|
followSharer
|
||||||
:: (MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
|
:: (MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
|
||||||
=> ShrIdent -> ShrIdent -> Bool -> m (TextHtml, Audience URIMode, AP.Follow URIMode)
|
=> ShrIdent -> ShrIdent -> Bool -> m (TextHtml, Audience URIMode, AP.Follow URIMode)
|
||||||
followSharer shrAuthor shrObject hide = do
|
followSharer shrAuthor shrObject hide = do
|
||||||
|
error "Temporarily disabled"
|
||||||
|
{-
|
||||||
encodeRouteHome <- getEncodeRouteHome
|
encodeRouteHome <- getEncodeRouteHome
|
||||||
let uObject = encodeRouteHome $ SharerR shrObject
|
let uObject = encodeRouteHome $ SharerR shrObject
|
||||||
follow shrAuthor uObject uObject hide
|
follow shrAuthor uObject uObject hide
|
||||||
|
-}
|
||||||
|
|
||||||
followProject
|
followProject
|
||||||
:: (MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
|
:: (MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
|
||||||
=> ShrIdent -> ShrIdent -> PrjIdent -> Bool -> m (TextHtml, Audience URIMode, AP.Follow URIMode)
|
=> ShrIdent -> ShrIdent -> PrjIdent -> Bool -> m (TextHtml, Audience URIMode, AP.Follow URIMode)
|
||||||
followProject shrAuthor shrObject prjObject hide = do
|
followProject shrAuthor shrObject prjObject hide = do
|
||||||
|
error "Temporarily disabled"
|
||||||
|
{-
|
||||||
encodeRouteHome <- getEncodeRouteHome
|
encodeRouteHome <- getEncodeRouteHome
|
||||||
let uObject = encodeRouteHome $ ProjectR shrObject prjObject
|
let uObject = encodeRouteHome $ ProjectR shrObject prjObject
|
||||||
follow shrAuthor uObject uObject hide
|
follow shrAuthor uObject uObject hide
|
||||||
|
-}
|
||||||
|
|
||||||
followTicket
|
followTicket
|
||||||
:: (MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
|
:: (MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
|
||||||
=> ShrIdent -> ShrIdent -> PrjIdent -> KeyHashid LocalTicket -> Bool -> m (TextHtml, Audience URIMode, AP.Follow URIMode)
|
=> ShrIdent -> ShrIdent -> PrjIdent -> KeyHashid LocalTicket -> Bool -> m (TextHtml, Audience URIMode, AP.Follow URIMode)
|
||||||
followTicket shrAuthor shrObject prjObject numObject hide = do
|
followTicket shrAuthor shrObject prjObject numObject hide = do
|
||||||
|
error "Temporarily disabled"
|
||||||
|
{-
|
||||||
encodeRouteHome <- getEncodeRouteHome
|
encodeRouteHome <- getEncodeRouteHome
|
||||||
let uObject = encodeRouteHome $ ProjectTicketR shrObject prjObject numObject
|
let uObject = encodeRouteHome $ ProjectTicketR shrObject prjObject numObject
|
||||||
uRecip = encodeRouteHome $ ProjectR shrObject prjObject
|
uRecip = encodeRouteHome $ ProjectR shrObject prjObject
|
||||||
follow shrAuthor uObject uRecip hide
|
follow shrAuthor uObject uRecip hide
|
||||||
|
-}
|
||||||
|
|
||||||
followRepo
|
followRepo
|
||||||
:: (MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
|
:: (MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
|
||||||
=> ShrIdent -> ShrIdent -> RpIdent -> Bool -> m (TextHtml, Audience URIMode, AP.Follow URIMode)
|
=> ShrIdent -> ShrIdent -> RpIdent -> Bool -> m (TextHtml, Audience URIMode, AP.Follow URIMode)
|
||||||
followRepo shrAuthor shrObject rpObject hide = do
|
followRepo shrAuthor shrObject rpObject hide = do
|
||||||
|
error "Temporarily disabled"
|
||||||
|
{-
|
||||||
encodeRouteHome <- getEncodeRouteHome
|
encodeRouteHome <- getEncodeRouteHome
|
||||||
let uObject = encodeRouteHome $ RepoR shrObject rpObject
|
let uObject = encodeRouteHome $ RepoR shrObject rpObject
|
||||||
follow shrAuthor uObject uObject hide
|
follow shrAuthor uObject uObject hide
|
||||||
|
-}
|
||||||
|
|
||||||
offerTicket
|
offerTicket
|
||||||
:: (MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
|
:: (MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
|
||||||
=> ShrIdent -> TextHtml -> TextPandocMarkdown -> ShrIdent -> PrjIdent -> m (Either Text (TextHtml, Audience URIMode, AP.Ticket URIMode, FedURI))
|
=> ShrIdent -> TextHtml -> TextPandocMarkdown -> ShrIdent -> PrjIdent -> m (Either Text (TextHtml, Audience URIMode, AP.Ticket URIMode, FedURI))
|
||||||
offerTicket shrAuthor (TextHtml title) (TextPandocMarkdown desc) shr prj = runExceptT $ do
|
offerTicket shrAuthor (TextHtml title) (TextPandocMarkdown desc) shr prj = runExceptT $ do
|
||||||
|
error "Temporarily disabled"
|
||||||
|
{-
|
||||||
encodeRouteLocal <- getEncodeRouteLocal
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
encodeRouteHome <- getEncodeRouteHome
|
encodeRouteHome <- getEncodeRouteHome
|
||||||
descHtml <- ExceptT . pure $ renderPandocMarkdown desc
|
descHtml <- ExceptT . pure $ renderPandocMarkdown desc
|
||||||
|
@ -266,68 +287,6 @@ offerTicket shrAuthor (TextHtml title) (TextPandocMarkdown desc) shr prj = runEx
|
||||||
}
|
}
|
||||||
return (summary, audience, ticket, target)
|
return (summary, audience, ticket, target)
|
||||||
|
|
||||||
createTicket
|
|
||||||
:: (MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
|
|
||||||
=> ShrIdent
|
|
||||||
-> TextHtml
|
|
||||||
-> TextPandocMarkdown
|
|
||||||
-> FedURI
|
|
||||||
-> FedURI
|
|
||||||
-> m (Either Text (TextHtml, Audience URIMode, Create URIMode))
|
|
||||||
createTicket shrAuthor (TextHtml title) (TextPandocMarkdown desc) target context = runExceptT $ do
|
|
||||||
summary <-
|
|
||||||
TextHtml . TL.toStrict . renderHtml <$>
|
|
||||||
withUrlRenderer
|
|
||||||
[hamlet|
|
|
||||||
<p>
|
|
||||||
<a href=@{SharerR shrAuthor}>
|
|
||||||
#{shr2text shrAuthor}
|
|
||||||
\ opened a ticket on project #
|
|
||||||
<a href="#{renderObjURI context}"}>
|
|
||||||
#{renderObjURI context}
|
|
||||||
: #{preEscapedToHtml title}.
|
|
||||||
|]
|
|
||||||
|
|
||||||
encodeRouteHome <- getEncodeRouteHome
|
|
||||||
let recipsA = [target]
|
|
||||||
recipsC =
|
|
||||||
let ObjURI h (LocalURI lu) = context
|
|
||||||
in [ ObjURI h $ LocalURI $ lu <> "/followers"
|
|
||||||
, ObjURI h $ LocalURI $ lu <> "/team"
|
|
||||||
, encodeRouteHome $ SharerFollowersR shrAuthor
|
|
||||||
]
|
|
||||||
audience = Audience
|
|
||||||
{ audienceTo = recipsA ++ recipsC
|
|
||||||
, audienceBto = []
|
|
||||||
, audienceCc = []
|
|
||||||
, audienceBcc = []
|
|
||||||
, audienceGeneral = []
|
|
||||||
, audienceNonActors = recipsC
|
|
||||||
}
|
|
||||||
|
|
||||||
encodeRouteLocal <- getEncodeRouteLocal
|
|
||||||
hLocal <- asksSite siteInstanceHost
|
|
||||||
descHtml <- ExceptT . pure $ renderPandocMarkdown desc
|
|
||||||
let ticket = AP.Ticket
|
|
||||||
{ AP.ticketLocal = Nothing
|
|
||||||
, AP.ticketAttributedTo = encodeRouteLocal $ SharerR shrAuthor
|
|
||||||
, AP.ticketPublished = Nothing
|
|
||||||
, AP.ticketUpdated = Nothing
|
|
||||||
, AP.ticketContext = Just context
|
|
||||||
, AP.ticketSummary = TextHtml title
|
|
||||||
, AP.ticketContent = TextHtml descHtml
|
|
||||||
, AP.ticketSource = TextPandocMarkdown desc
|
|
||||||
, AP.ticketAssignedTo = Nothing
|
|
||||||
, AP.ticketResolved = Nothing
|
|
||||||
, AP.ticketAttachment = Nothing
|
|
||||||
}
|
|
||||||
create = Create
|
|
||||||
{ createObject = CreateTicket hLocal ticket
|
|
||||||
, createTarget = Just target
|
|
||||||
}
|
|
||||||
|
|
||||||
return (summary, audience, create)
|
|
||||||
|
|
||||||
resolve
|
resolve
|
||||||
:: (MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
|
:: (MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
|
||||||
=> ShrIdent
|
=> ShrIdent
|
||||||
|
@ -358,6 +317,7 @@ resolve shrUser uObject = runExceptT $ do
|
||||||
|
|
||||||
recips = map encodeRouteHome audLocal ++ audRemote
|
recips = map encodeRouteHome audLocal ++ audRemote
|
||||||
return (Nothing, Audience recips [] [] [] [] [], Resolve uObject)
|
return (Nothing, Audience recips [] [] [] [] [], Resolve uObject)
|
||||||
|
-}
|
||||||
|
|
||||||
undoFollow
|
undoFollow
|
||||||
:: (MonadUnliftIO m, MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
|
:: (MonadUnliftIO m, MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
|
||||||
|
@ -369,6 +329,8 @@ undoFollow
|
||||||
-> Route App
|
-> Route App
|
||||||
-> m (Either Text (TextHtml, Audience URIMode, Undo URIMode))
|
-> m (Either Text (TextHtml, Audience URIMode, Undo URIMode))
|
||||||
undoFollow shrAuthor pidAuthor getFsid typ objRoute recipRoute = runExceptT $ do
|
undoFollow shrAuthor pidAuthor getFsid typ objRoute recipRoute = runExceptT $ do
|
||||||
|
error "Temporarily disabled"
|
||||||
|
{-
|
||||||
encodeRouteLocal <- getEncodeRouteLocal
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
encodeRouteHome <- getEncodeRouteHome
|
encodeRouteHome <- getEncodeRouteHome
|
||||||
obiidFollow <- runSiteDBExcept $ do
|
obiidFollow <- runSiteDBExcept $ do
|
||||||
|
@ -395,6 +357,7 @@ undoFollow shrAuthor pidAuthor getFsid typ objRoute recipRoute = runExceptT $ do
|
||||||
}
|
}
|
||||||
audience = Audience [encodeRouteHome recipRoute] [] [] [] [] []
|
audience = Audience [encodeRouteHome recipRoute] [] [] [] [] []
|
||||||
return (summary, audience, undo)
|
return (summary, audience, undo)
|
||||||
|
-}
|
||||||
|
|
||||||
undoFollowSharer
|
undoFollowSharer
|
||||||
:: (MonadUnliftIO m, MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
|
:: (MonadUnliftIO m, MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
|
||||||
|
@ -403,6 +366,8 @@ undoFollowSharer
|
||||||
-> ShrIdent
|
-> ShrIdent
|
||||||
-> m (Either Text (TextHtml, Audience URIMode, Undo URIMode))
|
-> m (Either Text (TextHtml, Audience URIMode, Undo URIMode))
|
||||||
undoFollowSharer shrAuthor pidAuthor shrFollowee =
|
undoFollowSharer shrAuthor pidAuthor shrFollowee =
|
||||||
|
error "Temporarily disabled"
|
||||||
|
{-
|
||||||
undoFollow shrAuthor pidAuthor getFsid "sharer" objRoute objRoute
|
undoFollow shrAuthor pidAuthor getFsid "sharer" objRoute objRoute
|
||||||
where
|
where
|
||||||
objRoute = SharerR shrFollowee
|
objRoute = SharerR shrFollowee
|
||||||
|
@ -432,6 +397,7 @@ undoFollowProject shrAuthor pidAuthor shrFollowee prjFollowee =
|
||||||
mj <- lift $ getValBy $ UniqueProject prjFollowee sidFollowee
|
mj <- lift $ getValBy $ UniqueProject prjFollowee sidFollowee
|
||||||
j <- fromMaybeE mj "Unfollow target no such local project"
|
j <- fromMaybeE mj "Unfollow target no such local project"
|
||||||
lift $ actorFollowers <$> getJust (projectActor j)
|
lift $ actorFollowers <$> getJust (projectActor j)
|
||||||
|
-}
|
||||||
|
|
||||||
undoFollowTicket
|
undoFollowTicket
|
||||||
:: (MonadUnliftIO m, MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
|
:: (MonadUnliftIO m, MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
|
||||||
|
@ -442,6 +408,8 @@ undoFollowTicket
|
||||||
-> KeyHashid LocalTicket
|
-> KeyHashid LocalTicket
|
||||||
-> m (Either Text (TextHtml, Audience URIMode, Undo URIMode))
|
-> m (Either Text (TextHtml, Audience URIMode, Undo URIMode))
|
||||||
undoFollowTicket shrAuthor pidAuthor shrFollowee prjFollowee numFollowee =
|
undoFollowTicket shrAuthor pidAuthor shrFollowee prjFollowee numFollowee =
|
||||||
|
error "Temporarily disabled"
|
||||||
|
{-
|
||||||
undoFollow shrAuthor pidAuthor getFsid "project" objRoute recipRoute
|
undoFollow shrAuthor pidAuthor getFsid "project" objRoute recipRoute
|
||||||
where
|
where
|
||||||
objRoute = ProjectTicketR shrFollowee prjFollowee numFollowee
|
objRoute = ProjectTicketR shrFollowee prjFollowee numFollowee
|
||||||
|
@ -467,6 +435,7 @@ undoFollowTicket shrAuthor pidAuthor shrFollowee prjFollowee numFollowee =
|
||||||
unless (ticketProjectLocalProject tpl == jid) $
|
unless (ticketProjectLocalProject tpl == jid) $
|
||||||
throwE "Hashid doesn't match sharer/project"
|
throwE "Hashid doesn't match sharer/project"
|
||||||
return $ localTicketFollowers lt
|
return $ localTicketFollowers lt
|
||||||
|
-}
|
||||||
|
|
||||||
undoFollowRepo
|
undoFollowRepo
|
||||||
:: (MonadUnliftIO m, MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
|
:: (MonadUnliftIO m, MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
|
||||||
|
@ -476,6 +445,8 @@ undoFollowRepo
|
||||||
-> RpIdent
|
-> RpIdent
|
||||||
-> m (Either Text (TextHtml, Audience URIMode, Undo URIMode))
|
-> m (Either Text (TextHtml, Audience URIMode, Undo URIMode))
|
||||||
undoFollowRepo shrAuthor pidAuthor shrFollowee rpFollowee =
|
undoFollowRepo shrAuthor pidAuthor shrFollowee rpFollowee =
|
||||||
|
error "Temporarily disabled"
|
||||||
|
{-
|
||||||
undoFollow shrAuthor pidAuthor getFsid "repo" objRoute objRoute
|
undoFollow shrAuthor pidAuthor getFsid "repo" objRoute objRoute
|
||||||
where
|
where
|
||||||
objRoute = RepoR shrFollowee rpFollowee
|
objRoute = RepoR shrFollowee rpFollowee
|
||||||
|
@ -486,6 +457,7 @@ undoFollowRepo shrAuthor pidAuthor shrFollowee rpFollowee =
|
||||||
mr <- lift $ getValBy $ UniqueRepo rpFollowee sidFollowee
|
mr <- lift $ getValBy $ UniqueRepo rpFollowee sidFollowee
|
||||||
repoFollowers <$>
|
repoFollowers <$>
|
||||||
fromMaybeE mr "Unfollow target no such local repo"
|
fromMaybeE mr "Unfollow target no such local repo"
|
||||||
|
-}
|
||||||
|
|
||||||
unresolve
|
unresolve
|
||||||
:: (MonadUnliftIO m, MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
|
:: (MonadUnliftIO m, MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
|
||||||
|
@ -493,6 +465,8 @@ unresolve
|
||||||
-> FedURI
|
-> FedURI
|
||||||
-> m (Either Text (Maybe TextHtml, Audience URIMode, Undo URIMode))
|
-> m (Either Text (Maybe TextHtml, Audience URIMode, Undo URIMode))
|
||||||
unresolve shrUser uTicket = runExceptT $ do
|
unresolve shrUser uTicket = runExceptT $ do
|
||||||
|
error "Temporarily disabled"
|
||||||
|
{-
|
||||||
encodeRouteHome <- getEncodeRouteHome
|
encodeRouteHome <- getEncodeRouteHome
|
||||||
wiFollowers <- askWorkItemFollowers
|
wiFollowers <- askWorkItemFollowers
|
||||||
ticket <- parseWorkItem "Ticket" uTicket
|
ticket <- parseWorkItem "Ticket" uTicket
|
||||||
|
@ -550,75 +524,7 @@ unresolve shrUser uTicket = runExceptT $ do
|
||||||
|
|
||||||
recips = map encodeRouteHome audLocal ++ audRemote
|
recips = map encodeRouteHome audLocal ++ audRemote
|
||||||
return (Nothing, Audience recips [] [] [] [] [], Undo uResolve)
|
return (Nothing, Audience recips [] [] [] [] [], Undo uResolve)
|
||||||
|
-}
|
||||||
createMR
|
|
||||||
:: (MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
|
|
||||||
=> ShrIdent
|
|
||||||
-> TextHtml
|
|
||||||
-> TextPandocMarkdown
|
|
||||||
-> FedURI
|
|
||||||
-> Maybe FedURI
|
|
||||||
-> PatchMediaType
|
|
||||||
-> Text
|
|
||||||
-> m (Either Text (Maybe TextHtml, Audience URIMode, AP.Ticket URIMode, Maybe FedURI))
|
|
||||||
createMR shrAuthor title desc uContext muBranch typ diff = runExceptT $ do
|
|
||||||
encodeRouteLocal <- getEncodeRouteLocal
|
|
||||||
encodeRouteHome <- getEncodeRouteHome
|
|
||||||
manager <- asksSite appHttpManager
|
|
||||||
hLocal <- asksSite siteInstanceHost
|
|
||||||
|
|
||||||
context <- parseTicketContext uContext
|
|
||||||
descHtml <-
|
|
||||||
ExceptT . pure $ renderPandocMarkdown $ unTextPandocMarkdown desc
|
|
||||||
context' <- bitraverse pure (getRemoteContextHttp "Context") context
|
|
||||||
|
|
||||||
let audAuthor =
|
|
||||||
AudLocal
|
|
||||||
[]
|
|
||||||
[LocalPersonCollectionSharerFollowers shrAuthor]
|
|
||||||
audContext = contextAudience context'
|
|
||||||
|
|
||||||
uTarget =
|
|
||||||
case context' of
|
|
||||||
Left _ -> uContext
|
|
||||||
Right (uTracker, _, _, _) -> uTracker
|
|
||||||
(_, _, _, audLocal, audRemote) =
|
|
||||||
collectAudience $ audAuthor : audContext
|
|
||||||
|
|
||||||
recips = map encodeRouteHome audLocal ++ audRemote
|
|
||||||
ObjURI hBranch luBranch = fromMaybe uContext muBranch
|
|
||||||
luAuthor = encodeRouteLocal $ SharerR shrAuthor
|
|
||||||
|
|
||||||
ticket = AP.Ticket
|
|
||||||
{ AP.ticketLocal = Nothing
|
|
||||||
, AP.ticketAttributedTo = luAuthor
|
|
||||||
, AP.ticketPublished = Nothing
|
|
||||||
, AP.ticketUpdated = Nothing
|
|
||||||
, AP.ticketContext = Just uContext
|
|
||||||
, AP.ticketSummary = title
|
|
||||||
, AP.ticketContent = TextHtml descHtml
|
|
||||||
, AP.ticketSource = desc
|
|
||||||
, AP.ticketAssignedTo = Nothing
|
|
||||||
, AP.ticketResolved = Nothing
|
|
||||||
, AP.ticketAttachment = Just
|
|
||||||
( hBranch
|
|
||||||
, MergeRequest
|
|
||||||
{ mrOrigin = Nothing
|
|
||||||
, mrTarget = luBranch
|
|
||||||
, mrBundle = Right
|
|
||||||
( hLocal
|
|
||||||
, BundleOffer Nothing $ pure AP.Patch
|
|
||||||
{ AP.patchLocal = Nothing
|
|
||||||
, AP.patchAttributedTo = luAuthor
|
|
||||||
, AP.patchPublished = Nothing
|
|
||||||
, AP.patchType = typ
|
|
||||||
, AP.patchContent = diff
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return (Nothing, Audience recips [] [] [] [] [], ticket, Just uTarget)
|
|
||||||
|
|
||||||
offerMR
|
offerMR
|
||||||
:: (MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
|
:: (MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
|
||||||
|
@ -631,6 +537,8 @@ offerMR
|
||||||
-> Text
|
-> Text
|
||||||
-> m (Either Text (Maybe TextHtml, Audience URIMode, AP.Ticket URIMode))
|
-> m (Either Text (Maybe TextHtml, Audience URIMode, AP.Ticket URIMode))
|
||||||
offerMR shrAuthor title desc uContext muBranch typ diff = runExceptT $ do
|
offerMR shrAuthor title desc uContext muBranch typ diff = runExceptT $ do
|
||||||
|
error "Temporarily disabled"
|
||||||
|
{-
|
||||||
encodeRouteLocal <- getEncodeRouteLocal
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
encodeRouteHome <- getEncodeRouteHome
|
encodeRouteHome <- getEncodeRouteHome
|
||||||
manager <- asksSite appHttpManager
|
manager <- asksSite appHttpManager
|
||||||
|
@ -684,6 +592,7 @@ offerMR shrAuthor title desc uContext muBranch typ diff = runExceptT $ do
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return (Nothing, Audience recips [] [] [] [] [], ticket)
|
return (Nothing, Audience recips [] [] [] [] [], ticket)
|
||||||
|
-}
|
||||||
|
|
||||||
createDeck
|
createDeck
|
||||||
:: (MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
|
:: (MonadHandler m, HandlerSite m ~ App, MonadSite m, SiteEnv m ~ App)
|
||||||
|
@ -692,6 +601,8 @@ createDeck
|
||||||
-> Maybe Text
|
-> Maybe Text
|
||||||
-> m (Maybe TextHtml, Audience URIMode, AP.ActorDetail, Maybe FedURI)
|
-> m (Maybe TextHtml, Audience URIMode, AP.ActorDetail, Maybe FedURI)
|
||||||
createDeck shrAuthor name mdesc = do
|
createDeck shrAuthor name mdesc = do
|
||||||
|
error "Temporarily disabled"
|
||||||
|
{-
|
||||||
encodeRouteHome <- getEncodeRouteHome
|
encodeRouteHome <- getEncodeRouteHome
|
||||||
|
|
||||||
let audAuthor =
|
let audAuthor =
|
||||||
|
@ -709,3 +620,4 @@ createDeck shrAuthor name mdesc = do
|
||||||
}
|
}
|
||||||
|
|
||||||
return (Nothing, Audience recips [] [] [] [] [], detail, Nothing)
|
return (Nothing, Audience recips [] [] [] [] [], detail, Nothing)
|
||||||
|
-}
|
||||||
|
|
133
src/Vervis/Cloth.hs
Normal file
133
src/Vervis/Cloth.hs
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
{- This file is part of Vervis.
|
||||||
|
-
|
||||||
|
- Written in 2020, 2022 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.Cloth
|
||||||
|
( getCloth
|
||||||
|
, getCloth404
|
||||||
|
)
|
||||||
|
where
|
||||||
|
|
||||||
|
import Control.Monad
|
||||||
|
import Control.Monad.IO.Class
|
||||||
|
import Control.Monad.Trans.Class
|
||||||
|
import Control.Monad.Trans.Maybe
|
||||||
|
import Control.Monad.Trans.Reader
|
||||||
|
import Data.List.NonEmpty (NonEmpty, nonEmpty)
|
||||||
|
import Data.Maybe
|
||||||
|
import Data.Traversable
|
||||||
|
import Database.Persist
|
||||||
|
import Database.Persist.Sql
|
||||||
|
import Yesod.Core
|
||||||
|
|
||||||
|
import Yesod.Hashids
|
||||||
|
|
||||||
|
import Data.Either.Local
|
||||||
|
import Database.Persist.Local
|
||||||
|
|
||||||
|
import Vervis.Foundation
|
||||||
|
import Vervis.Model
|
||||||
|
|
||||||
|
getCloth
|
||||||
|
:: MonadIO m
|
||||||
|
=> LoomId
|
||||||
|
-> TicketLoomId
|
||||||
|
-> ReaderT SqlBackend m
|
||||||
|
( Maybe
|
||||||
|
( Entity Loom
|
||||||
|
, Entity TicketLoom
|
||||||
|
, Entity Ticket
|
||||||
|
, Either (Entity TicketAuthorLocal) (Entity TicketAuthorRemote)
|
||||||
|
, Maybe
|
||||||
|
( Entity TicketResolve
|
||||||
|
, Either
|
||||||
|
(Entity TicketResolveLocal)
|
||||||
|
(Entity TicketResolveRemote)
|
||||||
|
)
|
||||||
|
, NonEmpty BundleId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
getCloth lid tlid = runMaybeT $ do
|
||||||
|
l <- MaybeT $ get lid
|
||||||
|
tl <- MaybeT $ get tlid
|
||||||
|
guard $ ticketLoomLoom tl == lid
|
||||||
|
|
||||||
|
let tid = ticketLoomTicket tl
|
||||||
|
t <- lift $ getJust tid
|
||||||
|
|
||||||
|
bnids <- lift $ do
|
||||||
|
mne <-
|
||||||
|
nonEmpty <$> selectKeysList [BundleTicket ==. tlid] [Desc BundleId]
|
||||||
|
case mne of
|
||||||
|
Nothing -> error "Found Loom Ticket without any Bundles"
|
||||||
|
Just ne -> return ne
|
||||||
|
|
||||||
|
author <-
|
||||||
|
lift $
|
||||||
|
requireEitherAlt
|
||||||
|
(getBy $ UniqueTicketAuthorLocal tid)
|
||||||
|
(getBy $ UniqueTicketAuthorRemote tid)
|
||||||
|
"MR doesn't have author"
|
||||||
|
"MR has both local and remote author"
|
||||||
|
|
||||||
|
mresolved <- lift $ getResolved tid
|
||||||
|
|
||||||
|
return (Entity lid l, Entity tlid tl, Entity tid t, author, mresolved, bnids)
|
||||||
|
|
||||||
|
where
|
||||||
|
|
||||||
|
getResolved
|
||||||
|
:: MonadIO m
|
||||||
|
=> TicketId
|
||||||
|
-> ReaderT SqlBackend m
|
||||||
|
(Maybe
|
||||||
|
( Entity TicketResolve
|
||||||
|
, Either
|
||||||
|
(Entity TicketResolveLocal)
|
||||||
|
(Entity TicketResolveRemote)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
getResolved tid = do
|
||||||
|
metr <- getBy $ UniqueTicketResolve tid
|
||||||
|
for metr $ \ etr@(Entity trid _) ->
|
||||||
|
(etr,) <$>
|
||||||
|
requireEitherAlt
|
||||||
|
(getBy $ UniqueTicketResolveLocal trid)
|
||||||
|
(getBy $ UniqueTicketResolveRemote trid)
|
||||||
|
"No TRX"
|
||||||
|
"Both TRL and TRR"
|
||||||
|
|
||||||
|
getCloth404
|
||||||
|
:: KeyHashid Loom
|
||||||
|
-> KeyHashid TicketLoom
|
||||||
|
-> AppDB
|
||||||
|
( Entity Loom
|
||||||
|
, Entity TicketLoom
|
||||||
|
, Entity Ticket
|
||||||
|
, Either (Entity TicketAuthorLocal) (Entity TicketAuthorRemote)
|
||||||
|
, Maybe
|
||||||
|
( Entity TicketResolve
|
||||||
|
, Either
|
||||||
|
(Entity TicketResolveLocal)
|
||||||
|
(Entity TicketResolveRemote)
|
||||||
|
)
|
||||||
|
, NonEmpty BundleId
|
||||||
|
)
|
||||||
|
getCloth404 lkhid tlkhid = do
|
||||||
|
lid <- decodeKeyHashid404 lkhid
|
||||||
|
tlid <- decodeKeyHashid404 tlkhid
|
||||||
|
mcloth <- getCloth lid tlid
|
||||||
|
case mcloth of
|
||||||
|
Nothing -> notFound
|
||||||
|
Just cloth -> return cloth
|
|
@ -15,13 +15,13 @@
|
||||||
-}
|
-}
|
||||||
|
|
||||||
module Vervis.Darcs
|
module Vervis.Darcs
|
||||||
( readSourceView
|
( --readSourceView
|
||||||
, readWikiView
|
--, readWikiView
|
||||||
, readChangesView
|
--, readChangesView
|
||||||
, lastChange
|
--, lastChange
|
||||||
, readPatch
|
--, readPatch
|
||||||
, writePostApplyHooks
|
writePostApplyHooks
|
||||||
, applyDarcsPatch
|
--, applyDarcsPatch
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
|
@ -45,6 +45,7 @@ import Data.Text.Encoding (encodeUtf8, decodeUtf8With, decodeUtf8)
|
||||||
import Data.Text.Encoding.Error (strictDecode)
|
import Data.Text.Encoding.Error (strictDecode)
|
||||||
import Data.Time.Clock (UTCTime, getCurrentTime, diffUTCTime)
|
import Data.Time.Clock (UTCTime, getCurrentTime, diffUTCTime)
|
||||||
import Data.Traversable (for)
|
import Data.Traversable (for)
|
||||||
|
import Database.Persist
|
||||||
import Development.Darcs.Internal.Hash.Codec
|
import Development.Darcs.Internal.Hash.Codec
|
||||||
import Development.Darcs.Internal.Hash.Types
|
import Development.Darcs.Internal.Hash.Types
|
||||||
import Development.Darcs.Internal.Inventory.Parser
|
import Development.Darcs.Internal.Inventory.Parser
|
||||||
|
@ -71,6 +72,7 @@ import qualified Development.Darcs.Internal.Patch.Parser as P
|
||||||
|
|
||||||
import Network.FedURI
|
import Network.FedURI
|
||||||
import Yesod.ActivityPub
|
import Yesod.ActivityPub
|
||||||
|
import Yesod.Hashids
|
||||||
import Yesod.MonadSite
|
import Yesod.MonadSite
|
||||||
|
|
||||||
import Darcs.Local.Repository
|
import Darcs.Local.Repository
|
||||||
|
@ -94,8 +96,8 @@ import Vervis.Path
|
||||||
import Vervis.Readme
|
import Vervis.Readme
|
||||||
import Vervis.Settings
|
import Vervis.Settings
|
||||||
import Vervis.SourceTree
|
import Vervis.SourceTree
|
||||||
import Vervis.Wiki (WikiView (..))
|
|
||||||
|
|
||||||
|
{-
|
||||||
dirToAnchoredPath :: [EntryName] -> AnchoredPath
|
dirToAnchoredPath :: [EntryName] -> AnchoredPath
|
||||||
dirToAnchoredPath = AnchoredPath . map (decodeWhiteName . encodeUtf8)
|
dirToAnchoredPath = AnchoredPath . map (decodeWhiteName . encodeUtf8)
|
||||||
|
|
||||||
|
@ -164,7 +166,9 @@ readSourceView path dir = do
|
||||||
let mitem = find expandedTree anch
|
let mitem = find expandedTree anch
|
||||||
for mitem $ itemToSourceView (last dir)
|
for mitem $ itemToSourceView (last dir)
|
||||||
return $ renderSources dir <$> msv
|
return $ renderSources dir <$> msv
|
||||||
|
-}
|
||||||
|
|
||||||
|
{-
|
||||||
readWikiView
|
readWikiView
|
||||||
:: (EntryName -> EntryName -> Maybe Text)
|
:: (EntryName -> EntryName -> Maybe Text)
|
||||||
-- ^ Page name predicate. Returns 'Nothing' for a file which isn't a page.
|
-- ^ Page name predicate. Returns 'Nothing' for a file which isn't a page.
|
||||||
|
@ -214,7 +218,9 @@ readWikiView isPage isMain path dir = do
|
||||||
mkview Nothing b = WikiViewRaw b
|
mkview Nothing b = WikiViewRaw b
|
||||||
mkview (Just mt) b = WikiViewPage mt b
|
mkview (Just mt) b = WikiViewPage mt b
|
||||||
for mpage $ \ (load, mmtitle) -> mkview mmtitle <$> load
|
for mpage $ \ (load, mmtitle) -> mkview mmtitle <$> load
|
||||||
|
-}
|
||||||
|
|
||||||
|
{-
|
||||||
readChangesView
|
readChangesView
|
||||||
:: FilePath
|
:: FilePath
|
||||||
-- ^ Repository path
|
-- ^ Repository path
|
||||||
|
@ -383,20 +389,20 @@ readPatch path hash = handle $ runExceptT $ do
|
||||||
mkedit' (Replace fp regex old new) = AddTextFile "Replace" 0 [T.concat ["replace ", T.pack fp, " ", regex, " ", old, " ", new]]
|
mkedit' (Replace fp regex old new) = AddTextFile "Replace" 0 [T.concat ["replace ", T.pack fp, " ", regex, " ", old, " ", new]]
|
||||||
mkedit' (Binary fp old new) = EditBinaryFile fp (fromIntegral $ B.length old) 0 (fromIntegral $ B.length new) 0
|
mkedit' (Binary fp old new) = EditBinaryFile fp (fromIntegral $ B.length old) 0 (fromIntegral $ B.length new) 0
|
||||||
mkedit' (Pref pref old new) = AddTextFile "Pref" 0 [T.concat ["changepref ", pref, " ", old, " ", new]]
|
mkedit' (Pref pref old new) = AddTextFile "Pref" 0 [T.concat ["changepref ", pref, " ", old, " ", new]]
|
||||||
|
-}
|
||||||
|
|
||||||
writePostApplyHooks :: WorkerDB ()
|
writePostApplyHooks :: WorkerDB ()
|
||||||
writePostApplyHooks = do
|
writePostApplyHooks = do
|
||||||
repos <- E.select $ E.from $ \ (r `E.InnerJoin` s) -> do
|
|
||||||
E.on $ r E.^. RepoSharer E.==. s E.^. SharerId
|
|
||||||
E.where_ $ r E.^. RepoVcs E.==. E.val VCSDarcs
|
|
||||||
return (s E.^. SharerIdent, r E.^. RepoIdent)
|
|
||||||
hook <- asksSite $ appPostApplyHookFile . appSettings
|
hook <- asksSite $ appPostApplyHookFile . appSettings
|
||||||
authority <- asksSite $ renderAuthority . siteInstanceHost
|
authority <- asksSite $ renderAuthority . siteInstanceHost
|
||||||
for_ repos $ \ (E.Value shr, E.Value rp) -> do
|
repos <- selectKeysList [RepoVcs ==. VCSDarcs] []
|
||||||
path <- askRepoDir shr rp
|
for_ repos $ \ repoID -> do
|
||||||
|
repoHash <- encodeKeyHashid repoID
|
||||||
|
path <- askRepoDir repoHash
|
||||||
liftIO $
|
liftIO $
|
||||||
writeDefaultsFile path hook authority (shr2text shr) (rp2text rp)
|
writeDefaultsFile path hook authority (keyHashidText repoHash)
|
||||||
|
|
||||||
|
{-
|
||||||
applyDarcsPatch shr rp patch = do
|
applyDarcsPatch shr rp patch = do
|
||||||
path <- askRepoDir shr rp
|
path <- askRepoDir shr rp
|
||||||
let input = BL.fromStrict $ TE.encodeUtf8 patch
|
let input = BL.fromStrict $ TE.encodeUtf8 patch
|
||||||
|
@ -414,3 +420,4 @@ applyDarcsPatch shr rp patch = do
|
||||||
, "\nstderr: ", out2text err
|
, "\nstderr: ", out2text err
|
||||||
]
|
]
|
||||||
ExitSuccess -> return ()
|
ExitSuccess -> return ()
|
||||||
|
-}
|
||||||
|
|
807
src/Vervis/Delivery.hs
Normal file
807
src/Vervis/Delivery.hs
Normal file
|
@ -0,0 +1,807 @@
|
||||||
|
{- This file is part of Vervis.
|
||||||
|
-
|
||||||
|
- Written in 2019, 2020, 2021, 2022 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.Delivery
|
||||||
|
( deliverHttp
|
||||||
|
, deliverHttpBL
|
||||||
|
, deliverRemoteDB_D
|
||||||
|
, deliverRemoteDB_P
|
||||||
|
, deliverRemoteDB_R
|
||||||
|
, deliverRemoteHTTP_D
|
||||||
|
, deliverRemoteHTTP_P
|
||||||
|
, deliverRemoteHTTP_R
|
||||||
|
, deliverRemoteDB'
|
||||||
|
, deliverRemoteDB''
|
||||||
|
, deliverRemoteHttp
|
||||||
|
, deliverRemoteHttp'
|
||||||
|
, deliverLocal'
|
||||||
|
, deliverLocal
|
||||||
|
, insertRemoteActivityToLocalInboxes
|
||||||
|
)
|
||||||
|
where
|
||||||
|
|
||||||
|
import Control.Applicative
|
||||||
|
import Control.Exception hiding (Handler, try)
|
||||||
|
import Control.Monad
|
||||||
|
import Control.Monad.IO.Class
|
||||||
|
import Control.Monad.IO.Unlift
|
||||||
|
import Control.Monad.Logger.CallStack
|
||||||
|
import Control.Monad.Trans.Class
|
||||||
|
import Control.Monad.Trans.Except
|
||||||
|
import Control.Monad.Trans.Maybe
|
||||||
|
import Control.Monad.Trans.Reader
|
||||||
|
import Data.Bifunctor
|
||||||
|
import Data.Bitraversable
|
||||||
|
import Data.ByteString (ByteString)
|
||||||
|
import Data.Either
|
||||||
|
import Data.Foldable
|
||||||
|
import Data.Function
|
||||||
|
import Data.List.NonEmpty (NonEmpty (..), nonEmpty)
|
||||||
|
import Data.Maybe
|
||||||
|
import Data.Semigroup
|
||||||
|
import Data.Text (Text)
|
||||||
|
import Data.Text.Encoding
|
||||||
|
import Data.Time.Clock
|
||||||
|
import Data.Traversable
|
||||||
|
import Database.Persist
|
||||||
|
import Database.Persist.Sql
|
||||||
|
import Network.HTTP.Client
|
||||||
|
import Network.TLS -- hiding (SHA256)
|
||||||
|
import Text.Blaze.Html (preEscapedToHtml)
|
||||||
|
import Text.Blaze.Html.Renderer.Text
|
||||||
|
import UnliftIO.Exception (try)
|
||||||
|
import Yesod.Core hiding (logError, logWarn, logInfo, logDebug)
|
||||||
|
import Yesod.Core.Handler
|
||||||
|
import Yesod.Persist.Core
|
||||||
|
|
||||||
|
import qualified Data.ByteString.Lazy as BL
|
||||||
|
import qualified Data.CaseInsensitive as CI
|
||||||
|
import qualified Data.List.NonEmpty as NE
|
||||||
|
import qualified Data.List as L
|
||||||
|
import qualified Data.List.Ordered as LO
|
||||||
|
import qualified Data.Text as T
|
||||||
|
import qualified Data.Text.Lazy as TL
|
||||||
|
import qualified Database.Esqueleto as E
|
||||||
|
|
||||||
|
import Yesod.HttpSignature
|
||||||
|
|
||||||
|
import Database.Persist.JSON
|
||||||
|
import Network.FedURI
|
||||||
|
import Network.HTTP.Digest
|
||||||
|
import Yesod.ActivityPub
|
||||||
|
import Yesod.MonadSite
|
||||||
|
import Yesod.FedURI
|
||||||
|
import Yesod.Hashids
|
||||||
|
|
||||||
|
import qualified Web.ActivityPub as AP
|
||||||
|
|
||||||
|
import Control.Monad.Trans.Except.Local
|
||||||
|
import Data.Either.Local
|
||||||
|
import Data.List.NonEmpty.Local
|
||||||
|
import Data.Patch.Local hiding (Patch)
|
||||||
|
import Data.Tuple.Local
|
||||||
|
import Database.Persist.Local
|
||||||
|
|
||||||
|
import qualified Data.Patch.Local as P
|
||||||
|
|
||||||
|
import Vervis.ActivityPub
|
||||||
|
import Vervis.FedURI
|
||||||
|
import Vervis.Foundation
|
||||||
|
import Vervis.Model
|
||||||
|
import Vervis.Model.Ident
|
||||||
|
import Vervis.Recipient
|
||||||
|
import Vervis.RemoteActorStore
|
||||||
|
import Vervis.Settings
|
||||||
|
import Vervis.Time
|
||||||
|
|
||||||
|
deliverHttp
|
||||||
|
:: (MonadSite m, SiteEnv m ~ App)
|
||||||
|
=> AP.Doc AP.Activity URIMode
|
||||||
|
-> Maybe LocalURI
|
||||||
|
-> Host
|
||||||
|
-> LocalURI
|
||||||
|
-> m (Either AP.APPostError (Response ()))
|
||||||
|
deliverHttp doc mfwd h luInbox =
|
||||||
|
deliverActivity (ObjURI h luInbox) (ObjURI h <$> mfwd) doc
|
||||||
|
|
||||||
|
deliverHttpBL
|
||||||
|
:: (MonadSite m, SiteEnv m ~ App)
|
||||||
|
=> BL.ByteString
|
||||||
|
-> Maybe LocalURI
|
||||||
|
-> Host
|
||||||
|
-> LocalURI
|
||||||
|
-> m (Either AP.APPostError (Response ()))
|
||||||
|
deliverHttpBL body mfwd h luInbox =
|
||||||
|
deliverActivityBL' (ObjURI h luInbox) (ObjURI h <$> mfwd) body
|
||||||
|
|
||||||
|
deliverRemoteDB_
|
||||||
|
:: (MonadIO m, PersistRecordBackend fwder SqlBackend)
|
||||||
|
=> (ForwardingId -> Key sender -> fwder)
|
||||||
|
-> BL.ByteString
|
||||||
|
-> RemoteActivityId
|
||||||
|
-> Key sender
|
||||||
|
-> ByteString
|
||||||
|
-> [((InstanceId, Host), NonEmpty RemoteRecipient)]
|
||||||
|
-> ReaderT SqlBackend m
|
||||||
|
[((InstanceId, Host), NonEmpty (RemoteActorId, LocalURI, LocalURI, ForwardingId, Key fwder))]
|
||||||
|
deliverRemoteDB_ makeFwder body ractid senderKey sig recips = do
|
||||||
|
let body' = BL.toStrict body
|
||||||
|
makeFwd (RemoteRecipient raid _ _ msince) =
|
||||||
|
Forwarding raid ractid body' sig (isNothing msince)
|
||||||
|
fetchedDeliv <- for recips $ bitraverse pure $ \ rs -> do
|
||||||
|
fwds <- insertMany' makeFwd rs
|
||||||
|
insertMany' (flip makeFwder senderKey . snd) fwds
|
||||||
|
return $ takeNoError5 fetchedDeliv
|
||||||
|
where
|
||||||
|
takeNoError noError = mapMaybe $ \ (i, rs) -> (i,) <$> nonEmpty (mapMaybe noError $ NE.toList rs)
|
||||||
|
takeNoError5 = takeNoError noError
|
||||||
|
where
|
||||||
|
noError ((RemoteRecipient ak luA luI Nothing , fwid), fwrid) = Just (ak, luA, luI, fwid, fwrid)
|
||||||
|
noError ((RemoteRecipient _ _ _ (Just _), _ ), _ ) = Nothing
|
||||||
|
|
||||||
|
deliverRemoteDB_D
|
||||||
|
:: MonadIO m
|
||||||
|
=> BL.ByteString
|
||||||
|
-> RemoteActivityId
|
||||||
|
-> DeckId
|
||||||
|
-> ByteString
|
||||||
|
-> [((InstanceId, Host), NonEmpty RemoteRecipient)]
|
||||||
|
-> ReaderT SqlBackend m
|
||||||
|
[((InstanceId, Host), NonEmpty (RemoteActorId, LocalURI, LocalURI, ForwardingId, ForwarderDeckId))]
|
||||||
|
deliverRemoteDB_D = deliverRemoteDB_ ForwarderDeck
|
||||||
|
|
||||||
|
deliverRemoteDB_P
|
||||||
|
:: MonadIO m
|
||||||
|
=> BL.ByteString
|
||||||
|
-> RemoteActivityId
|
||||||
|
-> PersonId
|
||||||
|
-> ByteString
|
||||||
|
-> [((InstanceId, Host), NonEmpty RemoteRecipient)]
|
||||||
|
-> ReaderT SqlBackend m
|
||||||
|
[((InstanceId, Host), NonEmpty (RemoteActorId, LocalURI, LocalURI, ForwardingId, ForwarderPersonId))]
|
||||||
|
deliverRemoteDB_P = deliverRemoteDB_ ForwarderPerson
|
||||||
|
|
||||||
|
deliverRemoteDB_R
|
||||||
|
:: MonadIO m
|
||||||
|
=> BL.ByteString
|
||||||
|
-> RemoteActivityId
|
||||||
|
-> RepoId
|
||||||
|
-> ByteString
|
||||||
|
-> [((InstanceId, Host), NonEmpty RemoteRecipient)]
|
||||||
|
-> ReaderT SqlBackend m
|
||||||
|
[((InstanceId, Host), NonEmpty (RemoteActorId, LocalURI, LocalURI, ForwardingId, ForwarderRepoId))]
|
||||||
|
deliverRemoteDB_R = deliverRemoteDB_ ForwarderRepo
|
||||||
|
|
||||||
|
deliverRemoteHTTP'
|
||||||
|
:: (MonadSite m, SiteEnv m ~ App, PersistRecordBackend fwder SqlBackend)
|
||||||
|
=> UTCTime
|
||||||
|
-> LocalActor
|
||||||
|
-> BL.ByteString
|
||||||
|
-> ByteString
|
||||||
|
-> [((InstanceId, Host), NonEmpty (RemoteActorId, LocalURI, LocalURI, ForwardingId, Key fwder))]
|
||||||
|
-> m ()
|
||||||
|
deliverRemoteHTTP' now sender body sig fetched = do
|
||||||
|
let deliver h inbox =
|
||||||
|
forwardActivity (ObjURI h inbox) sig (renderLocalActor sender) body
|
||||||
|
traverse_ (fork . deliverFetched deliver now) fetched
|
||||||
|
where
|
||||||
|
fork = forkWorker "Inbox forwarding to remote members of local collections: delivery failed"
|
||||||
|
deliverFetched deliver now ((_, h), recips@(r :| rs)) = do
|
||||||
|
let (raid, _luActor, luInbox, fwid, forwarderKey) = r
|
||||||
|
e <- deliver h luInbox
|
||||||
|
let e' = case e of
|
||||||
|
Left err ->
|
||||||
|
if isInstanceErrorP err
|
||||||
|
then Nothing
|
||||||
|
else Just False
|
||||||
|
Right _resp -> Just True
|
||||||
|
case e' of
|
||||||
|
Nothing -> runSiteDB $ do
|
||||||
|
let recips' = NE.toList recips
|
||||||
|
updateWhere [RemoteActorId <-. map fst5 recips', RemoteActorErrorSince ==. Nothing] [RemoteActorErrorSince =. Just now]
|
||||||
|
updateWhere [ForwardingId <-. map fourth5 recips'] [ForwardingRunning =. False]
|
||||||
|
Just success -> do
|
||||||
|
runSiteDB $
|
||||||
|
if success
|
||||||
|
then do
|
||||||
|
delete forwarderKey
|
||||||
|
delete fwid
|
||||||
|
else do
|
||||||
|
updateWhere [RemoteActorId ==. raid, RemoteActorErrorSince ==. Nothing] [RemoteActorErrorSince =. Just now]
|
||||||
|
update fwid [ForwardingRunning =. False]
|
||||||
|
for_ rs $ \ (raid, _luActor, luInbox, fwid, forwarderKey) ->
|
||||||
|
fork $ do
|
||||||
|
e <- deliver h luInbox
|
||||||
|
runSiteDB $
|
||||||
|
case e of
|
||||||
|
Left _err -> do
|
||||||
|
updateWhere [RemoteActorId ==. raid, RemoteActorErrorSince ==. Nothing] [RemoteActorErrorSince =. Just now]
|
||||||
|
update fwid [ForwardingRunning =. False]
|
||||||
|
Right _resp -> do
|
||||||
|
delete forwarderKey
|
||||||
|
delete fwid
|
||||||
|
|
||||||
|
deliverRemoteHTTP_D
|
||||||
|
:: (MonadSite m, SiteEnv m ~ App)
|
||||||
|
=> UTCTime
|
||||||
|
-> KeyHashid Deck
|
||||||
|
-> BL.ByteString
|
||||||
|
-> ByteString
|
||||||
|
-> [((InstanceId, Host), NonEmpty (RemoteActorId, LocalURI, LocalURI, ForwardingId, ForwarderDeckId))]
|
||||||
|
-> m ()
|
||||||
|
deliverRemoteHTTP_D now dkhid =
|
||||||
|
deliverRemoteHTTP' now $ LocalActorDeck dkhid
|
||||||
|
|
||||||
|
deliverRemoteHTTP_P
|
||||||
|
:: (MonadSite m, SiteEnv m ~ App)
|
||||||
|
=> UTCTime
|
||||||
|
-> KeyHashid Person
|
||||||
|
-> BL.ByteString
|
||||||
|
-> ByteString
|
||||||
|
-> [((InstanceId, Host), NonEmpty (RemoteActorId, LocalURI, LocalURI, ForwardingId, ForwarderPersonId))]
|
||||||
|
-> m ()
|
||||||
|
deliverRemoteHTTP_P now pkhid = deliverRemoteHTTP' now $ LocalActorPerson pkhid
|
||||||
|
|
||||||
|
deliverRemoteHTTP_R
|
||||||
|
:: (MonadSite m, SiteEnv m ~ App)
|
||||||
|
=> UTCTime
|
||||||
|
-> KeyHashid Repo
|
||||||
|
-> BL.ByteString
|
||||||
|
-> ByteString
|
||||||
|
-> [((InstanceId, Host), NonEmpty (RemoteActorId, LocalURI, LocalURI, ForwardingId, ForwarderRepoId))]
|
||||||
|
-> m ()
|
||||||
|
deliverRemoteHTTP_R now rkhid = deliverRemoteHTTP' now $ LocalActorRepo rkhid
|
||||||
|
|
||||||
|
deliverRemoteDB'
|
||||||
|
:: Host
|
||||||
|
-> OutboxItemId
|
||||||
|
-> [(Host, NonEmpty LocalURI)]
|
||||||
|
-> [((InstanceId, Host), NonEmpty RemoteRecipient)]
|
||||||
|
-> AppDB
|
||||||
|
( [((InstanceId, Host), NonEmpty (RemoteActorId, LocalURI, LocalURI, DeliveryId))]
|
||||||
|
, [((InstanceId, Host), NonEmpty (UnfetchedRemoteActorId, LocalURI, UnlinkedDeliveryId))]
|
||||||
|
, [((InstanceId, Host), NonEmpty (UnfetchedRemoteActorId, LocalURI, UnlinkedDeliveryId))]
|
||||||
|
)
|
||||||
|
deliverRemoteDB' hContext = deliverRemoteDB'' [hContext]
|
||||||
|
|
||||||
|
data Recip
|
||||||
|
= RecipRA (Entity RemoteActor)
|
||||||
|
| RecipURA (Entity UnfetchedRemoteActor)
|
||||||
|
| RecipRC (Entity RemoteCollection)
|
||||||
|
|
||||||
|
deliverRemoteDB''
|
||||||
|
:: MonadIO m
|
||||||
|
=> [Host]
|
||||||
|
-> OutboxItemId
|
||||||
|
-> [(Host, NonEmpty LocalURI)]
|
||||||
|
-> [((InstanceId, Host), NonEmpty RemoteRecipient)]
|
||||||
|
-> ReaderT SqlBackend m
|
||||||
|
( [((InstanceId, Host), NonEmpty (RemoteActorId, LocalURI, LocalURI, DeliveryId))]
|
||||||
|
, [((InstanceId, Host), NonEmpty (UnfetchedRemoteActorId, LocalURI, UnlinkedDeliveryId))]
|
||||||
|
, [((InstanceId, Host), NonEmpty (UnfetchedRemoteActorId, LocalURI, UnlinkedDeliveryId))]
|
||||||
|
)
|
||||||
|
deliverRemoteDB'' hContexts obid recips known = do
|
||||||
|
recips' <- for recips $ \ (h, lus) -> do
|
||||||
|
let lus' = NE.nub lus
|
||||||
|
(iid, inew) <- idAndNew <$> insertBy' (Instance h)
|
||||||
|
if inew
|
||||||
|
then return ((iid, h), (Nothing, Nothing, Just lus'))
|
||||||
|
else do
|
||||||
|
es <- for lus' $ \ lu -> do
|
||||||
|
ma <- runMaybeT $ do
|
||||||
|
Entity roid ro <- MaybeT $ getBy $ UniqueRemoteObject iid lu
|
||||||
|
recip <- RecipRA <$> MaybeT (getBy $ UniqueRemoteActor roid)
|
||||||
|
<|> RecipURA <$> MaybeT (getBy $ UniqueUnfetchedRemoteActor roid)
|
||||||
|
<|> RecipRC <$> MaybeT (getBy $ UniqueRemoteCollection roid)
|
||||||
|
return (ro, recip)
|
||||||
|
return $
|
||||||
|
case ma of
|
||||||
|
Nothing -> Just $ Left lu
|
||||||
|
Just (ro, r) ->
|
||||||
|
case r of
|
||||||
|
RecipRA (Entity raid ra) -> Just $ Right $ Left $ RemoteRecipient raid (remoteObjectIdent ro) (remoteActorInbox ra) (remoteActorErrorSince ra)
|
||||||
|
RecipURA (Entity uraid ura) -> Just $ Right $ Right (uraid, remoteObjectIdent ro, unfetchedRemoteActorSince ura)
|
||||||
|
RecipRC _ -> Nothing
|
||||||
|
let (unknown, newKnown) = partitionEithers $ catMaybes $ NE.toList es
|
||||||
|
(fetched, unfetched) = partitionEithers newKnown
|
||||||
|
return ((iid, h), (nonEmpty fetched, nonEmpty unfetched, nonEmpty unknown))
|
||||||
|
let moreKnown = mapMaybe (\ (i, (f, _, _)) -> (i,) <$> f) recips'
|
||||||
|
unfetched = mapMaybe (\ (i, (_, uf, _)) -> (i,) <$> uf) recips'
|
||||||
|
stillUnknown = mapMaybe (\ (i, (_, _, uk)) -> (i,) <$> uk) recips'
|
||||||
|
allFetched = unionRemotes known moreKnown
|
||||||
|
fetchedDeliv <- for allFetched $ \ (i, rs) ->
|
||||||
|
let fwd = snd i `elem` hContexts
|
||||||
|
in (i,) <$> insertMany' (\ (RemoteRecipient raid _ _ msince) -> Delivery raid obid fwd $ isNothing msince) rs
|
||||||
|
unfetchedDeliv <- for unfetched $ \ (i, rs) ->
|
||||||
|
let fwd = snd i `elem` hContexts
|
||||||
|
in (i,) <$> insertMany' (\ (uraid, _, msince) -> UnlinkedDelivery uraid obid fwd $ isNothing msince) rs
|
||||||
|
unknownDeliv <- for stillUnknown $ \ (i, lus) -> do
|
||||||
|
-- TODO maybe for URA insertion we should do insertUnique?
|
||||||
|
ros <- insertMany' (\ lu -> RemoteObject (fst i) lu) lus
|
||||||
|
rs <- insertMany' (\ (_lu, roid) -> UnfetchedRemoteActor roid Nothing) ros
|
||||||
|
let fwd = snd i `elem` hContexts
|
||||||
|
(i,) <$> insertMany' (\ (_, uraid) -> UnlinkedDelivery uraid obid fwd True) rs
|
||||||
|
return
|
||||||
|
( takeNoError4 fetchedDeliv
|
||||||
|
, takeNoError3 unfetchedDeliv
|
||||||
|
, map
|
||||||
|
(second $ NE.map $ \ (((lu, _roid), ak), dlk) -> (ak, lu, dlk))
|
||||||
|
unknownDeliv
|
||||||
|
)
|
||||||
|
where
|
||||||
|
takeNoError noError = mapMaybe $ \ (i, rs) -> (i,) <$> nonEmpty (mapMaybe noError $ NE.toList rs)
|
||||||
|
takeNoError3 = takeNoError noError
|
||||||
|
where
|
||||||
|
noError ((ak, lu, Nothing), dlk) = Just (ak, lu, dlk)
|
||||||
|
noError ((_ , _ , Just _ ), _ ) = Nothing
|
||||||
|
takeNoError4 = takeNoError noError
|
||||||
|
where
|
||||||
|
noError (RemoteRecipient ak luA luI Nothing , dlk) = Just (ak, luA, luI, dlk)
|
||||||
|
noError (RemoteRecipient _ _ _ (Just _), _ ) = Nothing
|
||||||
|
|
||||||
|
deliverRemoteHttp
|
||||||
|
:: Host
|
||||||
|
-> OutboxItemId
|
||||||
|
-> AP.Doc AP.Activity URIMode
|
||||||
|
-> ( [((InstanceId, Host), NonEmpty (RemoteActorId, LocalURI, LocalURI, DeliveryId))]
|
||||||
|
, [((InstanceId, Host), NonEmpty (UnfetchedRemoteActorId, LocalURI, UnlinkedDeliveryId))]
|
||||||
|
, [((InstanceId, Host), NonEmpty (UnfetchedRemoteActorId, LocalURI, UnlinkedDeliveryId))]
|
||||||
|
)
|
||||||
|
-> Worker ()
|
||||||
|
deliverRemoteHttp hContext = deliverRemoteHttp' [hContext]
|
||||||
|
|
||||||
|
deliverRemoteHttp'
|
||||||
|
:: [Host]
|
||||||
|
-> OutboxItemId
|
||||||
|
-> AP.Doc AP.Activity URIMode
|
||||||
|
-> ( [((InstanceId, Host), NonEmpty (RemoteActorId, LocalURI, LocalURI, DeliveryId))]
|
||||||
|
, [((InstanceId, Host), NonEmpty (UnfetchedRemoteActorId, LocalURI, UnlinkedDeliveryId))]
|
||||||
|
, [((InstanceId, Host), NonEmpty (UnfetchedRemoteActorId, LocalURI, UnlinkedDeliveryId))]
|
||||||
|
)
|
||||||
|
-> Worker ()
|
||||||
|
deliverRemoteHttp' hContexts obid doc (fetched, unfetched, unknown) = do
|
||||||
|
logDebug' "Starting"
|
||||||
|
let deliver fwd h inbox = do
|
||||||
|
let fwd' = if h `elem` hContexts then Just fwd else Nothing
|
||||||
|
(isJust fwd',) <$> deliverHttp doc fwd' h inbox
|
||||||
|
now <- liftIO getCurrentTime
|
||||||
|
logDebug' $
|
||||||
|
"Launching fetched " <> showHosts fetched
|
||||||
|
traverse_ (fork . deliverFetched deliver now) fetched
|
||||||
|
logDebug' $
|
||||||
|
"Launching unfetched " <> showHosts unfetched
|
||||||
|
traverse_ (fork . deliverUnfetched deliver now) unfetched
|
||||||
|
logDebug' $
|
||||||
|
"Launching unknown " <> showHosts unknown
|
||||||
|
traverse_ (fork . deliverUnfetched deliver now) unknown
|
||||||
|
logDebug' "Done (async delivery may still be running)"
|
||||||
|
where
|
||||||
|
showHosts = T.pack . show . map (renderAuthority . snd . fst)
|
||||||
|
logDebug' t = logDebug $ prefix <> t
|
||||||
|
where
|
||||||
|
prefix =
|
||||||
|
T.concat
|
||||||
|
[ "Outbox POST handler: deliverRemoteHttp obid#"
|
||||||
|
, T.pack $ show $ fromSqlKey obid
|
||||||
|
, ": "
|
||||||
|
]
|
||||||
|
fork = forkWorker "Outbox POST handler: HTTP delivery"
|
||||||
|
deliverFetched deliver now ((_, h), recips@(r :| rs)) = do
|
||||||
|
logDebug'' "Starting"
|
||||||
|
let (raid, luActor, luInbox, dlid) = r
|
||||||
|
(_, e) <- deliver luActor h luInbox
|
||||||
|
e' <- case e of
|
||||||
|
Left err -> do
|
||||||
|
logError $ T.concat
|
||||||
|
[ "Outbox DL delivery #", T.pack $ show dlid
|
||||||
|
, " error for <", renderObjURI $ ObjURI h luActor
|
||||||
|
, ">: ", T.pack $ displayException err
|
||||||
|
]
|
||||||
|
return $
|
||||||
|
if isInstanceErrorP err
|
||||||
|
then Nothing
|
||||||
|
else Just False
|
||||||
|
Right _resp -> return $ Just True
|
||||||
|
case e' of
|
||||||
|
Nothing -> runSiteDB $ do
|
||||||
|
let recips' = NE.toList recips
|
||||||
|
updateWhere [RemoteActorId <-. map fst4 recips', RemoteActorErrorSince ==. Nothing] [RemoteActorErrorSince =. Just now]
|
||||||
|
updateWhere [DeliveryId <-. map fourth4 recips'] [DeliveryRunning =. False]
|
||||||
|
Just success -> do
|
||||||
|
runSiteDB $
|
||||||
|
if success
|
||||||
|
then delete dlid
|
||||||
|
else do
|
||||||
|
updateWhere [RemoteActorId ==. raid, RemoteActorErrorSince ==. Nothing] [RemoteActorErrorSince =. Just now]
|
||||||
|
update dlid [DeliveryRunning =. False]
|
||||||
|
for_ rs $ \ (raid, luActor, luInbox, dlid) ->
|
||||||
|
fork $ do
|
||||||
|
(_, e) <- deliver luActor h luInbox
|
||||||
|
runSiteDB $
|
||||||
|
case e of
|
||||||
|
Left err -> do
|
||||||
|
logError $ T.concat
|
||||||
|
[ "Outbox DL delivery #", T.pack $ show dlid
|
||||||
|
, " error for <", renderObjURI $ ObjURI h luActor
|
||||||
|
, ">: ", T.pack $ displayException err
|
||||||
|
]
|
||||||
|
updateWhere [RemoteActorId ==. raid, RemoteActorErrorSince ==. Nothing] [RemoteActorErrorSince =. Just now]
|
||||||
|
update dlid [DeliveryRunning =. False]
|
||||||
|
Right _resp -> delete dlid
|
||||||
|
where
|
||||||
|
logDebug'' t = logDebug' $ T.concat ["deliverFetched ", renderAuthority h, t]
|
||||||
|
deliverUnfetched deliver now ((iid, h), recips@(r :| rs)) = do
|
||||||
|
logDebug'' "Starting"
|
||||||
|
let (uraid, luActor, udlid) = r
|
||||||
|
e <- fetchRemoteActor iid h luActor
|
||||||
|
let e' = case e of
|
||||||
|
Left err -> Just Nothing
|
||||||
|
Right (Left err) ->
|
||||||
|
if isInstanceErrorG err
|
||||||
|
then Nothing
|
||||||
|
else Just Nothing
|
||||||
|
Right (Right mera) -> Just $ Just mera
|
||||||
|
case e' of
|
||||||
|
Nothing -> runSiteDB $ do
|
||||||
|
let recips' = NE.toList recips
|
||||||
|
updateWhere [UnfetchedRemoteActorId <-. map fst3 recips', UnfetchedRemoteActorSince ==. Nothing] [UnfetchedRemoteActorSince =. Just now]
|
||||||
|
updateWhere [UnlinkedDeliveryId <-. map thd3 recips'] [UnlinkedDeliveryRunning =. False]
|
||||||
|
Just mmera -> do
|
||||||
|
for_ rs $ \ (uraid, luActor, udlid) ->
|
||||||
|
fork $ do
|
||||||
|
e <- fetchRemoteActor iid h luActor
|
||||||
|
case e of
|
||||||
|
Right (Right mera) ->
|
||||||
|
case mera of
|
||||||
|
Nothing -> runSiteDB $ delete udlid
|
||||||
|
Just (Entity raid ra) -> do
|
||||||
|
(fwd, e') <- deliver luActor h $ remoteActorInbox ra
|
||||||
|
runSiteDB $
|
||||||
|
case e' of
|
||||||
|
Left _ -> do
|
||||||
|
updateWhere [RemoteActorId ==. raid, RemoteActorErrorSince ==. Nothing] [RemoteActorErrorSince =. Just now]
|
||||||
|
delete udlid
|
||||||
|
insert_ $ Delivery raid obid fwd False
|
||||||
|
Right _ -> delete udlid
|
||||||
|
_ -> runSiteDB $ do
|
||||||
|
updateWhere [UnfetchedRemoteActorId ==. uraid, UnfetchedRemoteActorSince ==. Nothing] [UnfetchedRemoteActorSince =. Just now]
|
||||||
|
update udlid [UnlinkedDeliveryRunning =. False]
|
||||||
|
case mmera of
|
||||||
|
Nothing -> runSiteDB $ do
|
||||||
|
updateWhere [UnfetchedRemoteActorId ==. uraid, UnfetchedRemoteActorSince ==. Nothing] [UnfetchedRemoteActorSince =. Just now]
|
||||||
|
update udlid [UnlinkedDeliveryRunning =. False]
|
||||||
|
Just mera ->
|
||||||
|
case mera of
|
||||||
|
Nothing -> runSiteDB $ delete udlid
|
||||||
|
Just (Entity raid ra) -> do
|
||||||
|
(fwd, e'') <- deliver luActor h $ remoteActorInbox ra
|
||||||
|
runSiteDB $
|
||||||
|
case e'' of
|
||||||
|
Left _ -> do
|
||||||
|
updateWhere [RemoteActorId ==. raid, RemoteActorErrorSince ==. Nothing] [RemoteActorErrorSince =. Just now]
|
||||||
|
delete udlid
|
||||||
|
insert_ $ Delivery raid obid fwd False
|
||||||
|
Right _ -> delete udlid
|
||||||
|
where
|
||||||
|
logDebug'' t = logDebug' $ T.concat ["deliverUnfetched ", renderAuthority h, t]
|
||||||
|
|
||||||
|
-- | Given a list of local recipients, which may include actors and
|
||||||
|
-- collections,
|
||||||
|
--
|
||||||
|
-- * Insert activity to inboxes of actors
|
||||||
|
-- * If collections are listed, insert activity to the local members and return
|
||||||
|
-- the remote members
|
||||||
|
insertActivityToLocalInboxes
|
||||||
|
:: ( MonadSite m
|
||||||
|
, YesodHashids (SiteEnv m)
|
||||||
|
, PersistRecordBackend record SqlBackend
|
||||||
|
)
|
||||||
|
=> (InboxId -> InboxItemId -> record)
|
||||||
|
-- ^ Database record to insert as an new inbox item to each inbox
|
||||||
|
-> Bool
|
||||||
|
-- ^ Whether to deliver to collection only if owner actor is addressed
|
||||||
|
-> Maybe LocalActor
|
||||||
|
-- ^ An actor whose collections are excluded from requiring an owner, i.e.
|
||||||
|
-- even if owner is required, this actor's collections will be delivered
|
||||||
|
-- to, even if this actor isn't addressed. This is meant to be the
|
||||||
|
-- activity's author.
|
||||||
|
-> Maybe ActorId
|
||||||
|
-- ^ A un actor whose inbox to exclude from delivery, even if this actor is
|
||||||
|
-- listed in the recipient set. This is meant to be the activity's
|
||||||
|
-- author.
|
||||||
|
-> RecipientRoutes
|
||||||
|
-> ReaderT SqlBackend m [((InstanceId, Host), NonEmpty RemoteRecipient)]
|
||||||
|
insertActivityToLocalInboxes makeInboxItem requireOwner mauthor maidAuthor recips = do
|
||||||
|
|
||||||
|
-- Predicate for filtering addressed stages
|
||||||
|
--allowStage <- getAllowStage
|
||||||
|
|
||||||
|
-- Unhash actor and work item hashids
|
||||||
|
people <- unhashKeys $ recipPeople recips
|
||||||
|
groups <- unhashKeys $ recipGroups recips
|
||||||
|
repos <- unhashKeys $ recipRepos recips
|
||||||
|
decksAndTickets <- do
|
||||||
|
decks <- unhashKeys $ recipDecks recips
|
||||||
|
for decks $ \ (deckID, (DeckFamilyRoutes deck tickets)) ->
|
||||||
|
(deckID,) . (deck,) <$> unhashKeys tickets
|
||||||
|
loomsAndCloths <- do
|
||||||
|
looms <- unhashKeys $ recipLooms recips
|
||||||
|
for looms $ \ (loomID, (LoomFamilyRoutes loom cloths)) ->
|
||||||
|
(loomID,) . (loom,) <$> unhashKeys cloths
|
||||||
|
|
||||||
|
-- Grab local actor sets whose stages are allowed for delivery
|
||||||
|
isAuthor <- getIsAuthor
|
||||||
|
let allowStages'
|
||||||
|
:: (famili -> routes)
|
||||||
|
-> (routes -> Bool)
|
||||||
|
-> (Key record -> LocalActorBy Key)
|
||||||
|
-> (Key record, famili)
|
||||||
|
-> Bool
|
||||||
|
allowStages' = allowStages isAuthor
|
||||||
|
|
||||||
|
peopleForStages =
|
||||||
|
filter (allowStages' id routePerson LocalActorPerson) people
|
||||||
|
groupsForStages =
|
||||||
|
filter (allowStages' id routeGroup LocalActorGroup) groups
|
||||||
|
reposForStages =
|
||||||
|
filter (allowStages' id routeRepo LocalActorRepo) repos
|
||||||
|
decksAndTicketsForStages =
|
||||||
|
filter (allowStages' fst routeDeck LocalActorDeck) decksAndTickets
|
||||||
|
loomsAndClothsForStages =
|
||||||
|
filter (allowStages' fst routeLoom LocalActorLoom) loomsAndCloths
|
||||||
|
|
||||||
|
-- Grab local actors being addressed
|
||||||
|
let personIDsForSelf =
|
||||||
|
[ key | (key, routes) <- people, routePerson routes ]
|
||||||
|
groupIDsForSelf =
|
||||||
|
[ key | (key, routes) <- groups, routeGroup routes ]
|
||||||
|
repoIDsForSelf =
|
||||||
|
[ key | (key, routes) <- repos, routeRepo routes ]
|
||||||
|
deckIDsForSelf =
|
||||||
|
[ key | (key, (routes, _)) <- decksAndTickets, routeDeck routes ]
|
||||||
|
loomIDsForSelf =
|
||||||
|
[ key | (key, (routes, _)) <- loomsAndCloths, routeLoom routes ]
|
||||||
|
|
||||||
|
-- Grab actor actors whose followers are going to be delivered to
|
||||||
|
let personIDsForFollowers =
|
||||||
|
[ key | (key, routes) <- peopleForStages, routePersonFollowers routes ]
|
||||||
|
repoIDsForFollowers =
|
||||||
|
[ key | (key, routes) <- reposForStages, routeRepoFollowers routes ]
|
||||||
|
deckIDsForFollowers =
|
||||||
|
[ key | (key, (routes, _)) <- decksAndTicketsForStages, routeDeckFollowers routes ]
|
||||||
|
loomIDsForFollowers =
|
||||||
|
[ key | (key, (routes, _)) <- loomsAndClothsForStages, routeLoomFollowers routes ]
|
||||||
|
|
||||||
|
-- Grab tickets and cloths whose followers are going to be delivered to
|
||||||
|
let ticketSetsForFollowers =
|
||||||
|
mapMaybe
|
||||||
|
(\ (deckID, (_, tickets)) -> (deckID,) <$>
|
||||||
|
NE.nonEmpty
|
||||||
|
[ ticketDeckID | (ticketDeckID, routes) <- tickets
|
||||||
|
, routeTicketFollowers routes
|
||||||
|
]
|
||||||
|
)
|
||||||
|
decksAndTicketsForStages
|
||||||
|
clothSetsForFollowers =
|
||||||
|
mapMaybe
|
||||||
|
(\ (loomID, (_, cloths)) -> (loomID,) <$>
|
||||||
|
NE.nonEmpty
|
||||||
|
[ ticketLoomID | (ticketLoomID, routes) <- cloths
|
||||||
|
, routeClothFollowers routes
|
||||||
|
]
|
||||||
|
)
|
||||||
|
loomsAndClothsForStages
|
||||||
|
|
||||||
|
-- Get addressed Actor IDs from DB
|
||||||
|
actorIDsForSelf <- orderedUnion <$> sequenceA
|
||||||
|
[ selectActorIDsOrdered personActor PersonActor personIDsForSelf
|
||||||
|
, selectActorIDsOrdered groupActor GroupActor groupIDsForSelf
|
||||||
|
, selectActorIDsOrdered repoActor RepoActor repoIDsForSelf
|
||||||
|
, selectActorIDsOrdered deckActor DeckActor deckIDsForSelf
|
||||||
|
, selectActorIDsOrdered loomActor LoomActor loomIDsForSelf
|
||||||
|
]
|
||||||
|
|
||||||
|
-- Get actor and work item FollowerSet IDs from DB
|
||||||
|
followerSetIDs <- do
|
||||||
|
actorIDs <- concat <$> sequenceA
|
||||||
|
[ selectActorIDs personActor personIDsForFollowers
|
||||||
|
, selectActorIDs repoActor repoIDsForFollowers
|
||||||
|
, selectActorIDs deckActor deckIDsForFollowers
|
||||||
|
, selectActorIDs loomActor loomIDsForFollowers
|
||||||
|
]
|
||||||
|
ticketIDs <-
|
||||||
|
concat <$>
|
||||||
|
((++)
|
||||||
|
<$> traverse
|
||||||
|
(selectTicketIDs ticketDeckTicket TicketDeckDeck)
|
||||||
|
ticketSetsForFollowers
|
||||||
|
<*> traverse
|
||||||
|
(selectTicketIDs ticketLoomTicket TicketLoomLoom)
|
||||||
|
clothSetsForFollowers
|
||||||
|
)
|
||||||
|
(++)
|
||||||
|
<$> (map (actorFollowers . entityVal) <$>
|
||||||
|
selectList [ActorId <-. actorIDs] []
|
||||||
|
)
|
||||||
|
<*> (map (ticketFollowers . entityVal) <$>
|
||||||
|
selectList [TicketId <-. ticketIDs] []
|
||||||
|
)
|
||||||
|
|
||||||
|
-- Get the local and remote followers of the follower sets from DB
|
||||||
|
localFollowers <-
|
||||||
|
map (followActor . entityVal) <$>
|
||||||
|
selectList [FollowTarget <-. followerSetIDs] [Asc FollowActor]
|
||||||
|
remoteFollowers <- getRemoteFollowers followerSetIDs
|
||||||
|
|
||||||
|
-- Insert inbox items to all local recipients, i.e. the local actors
|
||||||
|
-- directly addressed or listed in a local stage addressed
|
||||||
|
let localRecipients =
|
||||||
|
let allLocal = LO.union localFollowers actorIDsForSelf
|
||||||
|
in case maidAuthor of
|
||||||
|
Nothing -> allLocal
|
||||||
|
Just actorID -> LO.minus' allLocal [actorID]
|
||||||
|
inboxIDs <-
|
||||||
|
map (actorInbox . entityVal) <$>
|
||||||
|
selectList [ActorId <-. localRecipients] []
|
||||||
|
inboxItemIDs <- insertMany $ replicate (length inboxIDs) $ InboxItem True
|
||||||
|
insertMany_ $ zipWith makeInboxItem inboxIDs inboxItemIDs
|
||||||
|
|
||||||
|
-- Return remote followers, to whom we need to deliver via HTTP
|
||||||
|
return remoteFollowers
|
||||||
|
where
|
||||||
|
orderedUnion = foldl' LO.union []
|
||||||
|
|
||||||
|
unhashKeys
|
||||||
|
:: ( MonadSite m
|
||||||
|
, YesodHashids (SiteEnv m)
|
||||||
|
, ToBackendKey SqlBackend record
|
||||||
|
)
|
||||||
|
=> [(KeyHashid record, routes)]
|
||||||
|
-> m [(Key record, routes)]
|
||||||
|
unhashKeys actorSets = do
|
||||||
|
unhash <- decodeKeyHashidPure <$> asksSite siteHashidsContext
|
||||||
|
return $ mapMaybe (unhashKey unhash) actorSets
|
||||||
|
where
|
||||||
|
unhashKey unhash (hash, famili) = (,famili) <$> unhash hash
|
||||||
|
|
||||||
|
getIsAuthor =
|
||||||
|
case mauthor of
|
||||||
|
Nothing -> pure $ const False
|
||||||
|
Just author -> maybe (const False) (==) <$> unhashLocalActor author
|
||||||
|
|
||||||
|
allowStages
|
||||||
|
:: (LocalActorBy Key -> Bool)
|
||||||
|
-> (famili -> routes)
|
||||||
|
-> (routes -> Bool)
|
||||||
|
-> (Key record -> LocalActorBy Key)
|
||||||
|
-> (Key record, famili)
|
||||||
|
-> Bool
|
||||||
|
allowStages isAuthor familyActor routeActor makeActor (actorID, famili)
|
||||||
|
= routeActor (familyActor famili)
|
||||||
|
|| not requireOwner
|
||||||
|
|| isAuthor (makeActor actorID)
|
||||||
|
|
||||||
|
selectActorIDs
|
||||||
|
:: (MonadIO m, PersistRecordBackend record SqlBackend)
|
||||||
|
=> (record -> ActorId)
|
||||||
|
-> [Key record]
|
||||||
|
-> ReaderT SqlBackend m [ActorId]
|
||||||
|
selectActorIDs grabActor ids =
|
||||||
|
map (grabActor . entityVal) <$> selectList [persistIdField <-. ids] []
|
||||||
|
|
||||||
|
selectActorIDsOrdered
|
||||||
|
:: (MonadIO m, PersistRecordBackend record SqlBackend)
|
||||||
|
=> (record -> ActorId)
|
||||||
|
-> EntityField record ActorId
|
||||||
|
-> [Key record]
|
||||||
|
-> ReaderT SqlBackend m [ActorId]
|
||||||
|
selectActorIDsOrdered grabActor actorField ids =
|
||||||
|
map (grabActor . entityVal) <$> selectList [persistIdField <-. ids] [Asc actorField]
|
||||||
|
|
||||||
|
selectTicketIDs
|
||||||
|
:: ( MonadIO m
|
||||||
|
, PersistRecordBackend tracker SqlBackend
|
||||||
|
, PersistRecordBackend item SqlBackend
|
||||||
|
)
|
||||||
|
=> (item -> TicketId)
|
||||||
|
-> EntityField item (Key tracker)
|
||||||
|
-> (Key tracker, NonEmpty (Key item))
|
||||||
|
-> ReaderT SqlBackend m [TicketId]
|
||||||
|
selectTicketIDs grabTicket trackerField (trackerID, workItemIDs) = do
|
||||||
|
maybeTracker <- get trackerID
|
||||||
|
case maybeTracker of
|
||||||
|
Nothing -> pure []
|
||||||
|
Just _ ->
|
||||||
|
map (grabTicket . entityVal) <$>
|
||||||
|
selectList [persistIdField <-. NE.toList workItemIDs, trackerField ==. trackerID] []
|
||||||
|
|
||||||
|
getRemoteFollowers
|
||||||
|
:: MonadIO m
|
||||||
|
=> [FollowerSetId]
|
||||||
|
-> ReaderT SqlBackend m
|
||||||
|
[((InstanceId, Host), NonEmpty RemoteRecipient)]
|
||||||
|
getRemoteFollowers fsids =
|
||||||
|
fmap groupRemotes $
|
||||||
|
E.select $ E.from $ \ (rf `E.InnerJoin` ra `E.InnerJoin` ro `E.InnerJoin` i) -> do
|
||||||
|
E.on $ ro E.^. RemoteObjectInstance E.==. i E.^. InstanceId
|
||||||
|
E.on $ ra E.^. RemoteActorIdent E.==. ro E.^. RemoteObjectId
|
||||||
|
E.on $ rf E.^. RemoteFollowActor E.==. ra E.^. RemoteActorId
|
||||||
|
E.where_ $ rf E.^. RemoteFollowTarget `E.in_` E.valList fsids
|
||||||
|
E.orderBy [E.asc $ i E.^. InstanceId, E.asc $ ra E.^. RemoteActorId]
|
||||||
|
return
|
||||||
|
( i E.^. InstanceId
|
||||||
|
, i E.^. InstanceHost
|
||||||
|
, ra E.^. RemoteActorId
|
||||||
|
, ro E.^. RemoteObjectIdent
|
||||||
|
, ra E.^. RemoteActorInbox
|
||||||
|
, ra E.^. RemoteActorErrorSince
|
||||||
|
)
|
||||||
|
where
|
||||||
|
groupRemotes = groupWithExtractBy ((==) `on` fst) fst snd . map toTuples
|
||||||
|
where
|
||||||
|
toTuples (E.Value iid, E.Value h, E.Value raid, E.Value luA, E.Value luI, E.Value ms) = ((iid, h), RemoteRecipient raid luA luI ms)
|
||||||
|
|
||||||
|
-- | Given a list of local recipients, which may include actors and
|
||||||
|
-- collections,
|
||||||
|
--
|
||||||
|
-- * Insert activity to inboxes of actors
|
||||||
|
-- * If collections are listed, insert activity to the local members and return
|
||||||
|
-- the remote members
|
||||||
|
deliverLocal'
|
||||||
|
:: (MonadSite m, YesodHashids (SiteEnv m))
|
||||||
|
=> Bool -- ^ Whether to deliver to collection only if owner actor is addressed
|
||||||
|
-> LocalActor
|
||||||
|
-> ActorId
|
||||||
|
-> OutboxItemId
|
||||||
|
-> RecipientRoutes
|
||||||
|
-> ReaderT SqlBackend m [((InstanceId, Host), NonEmpty RemoteRecipient)]
|
||||||
|
deliverLocal' requireOwner author aidAuthor obiid =
|
||||||
|
insertActivityToLocalInboxes makeItem requireOwner (Just author) (Just aidAuthor)
|
||||||
|
where
|
||||||
|
makeItem ibid ibiid = InboxItemLocal ibid obiid ibiid
|
||||||
|
|
||||||
|
-- | 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
|
||||||
|
:: KeyHashid Person
|
||||||
|
-> ActorId
|
||||||
|
-> OutboxItemId
|
||||||
|
-> RecipientRoutes
|
||||||
|
-> AppDB
|
||||||
|
[ ( (InstanceId, Host)
|
||||||
|
, NonEmpty RemoteRecipient
|
||||||
|
)
|
||||||
|
]
|
||||||
|
deliverLocal authorHash aidAuthor obiid
|
||||||
|
= deliverLocal' True (LocalActorPerson authorHash) aidAuthor obiid
|
||||||
|
. localRecipSieve sieve True
|
||||||
|
where
|
||||||
|
sieve = RecipientRoutes [(authorHash, PersonRoutes False True)] [] [] [] []
|
||||||
|
|
||||||
|
insertRemoteActivityToLocalInboxes
|
||||||
|
:: (MonadSite m, YesodHashids (SiteEnv m))
|
||||||
|
=> Bool
|
||||||
|
-> RemoteActivityId
|
||||||
|
-> RecipientRoutes
|
||||||
|
-> ReaderT SqlBackend m [((InstanceId, Host), NonEmpty RemoteRecipient)]
|
||||||
|
insertRemoteActivityToLocalInboxes requireOwner ractid =
|
||||||
|
insertActivityToLocalInboxes makeItem requireOwner Nothing Nothing
|
||||||
|
where
|
||||||
|
makeItem ibid ibiid = InboxItemRemote ibid ractid ibiid
|
|
@ -1,6 +1,6 @@
|
||||||
{- This file is part of Vervis.
|
{- This file is part of Vervis.
|
||||||
-
|
-
|
||||||
- Written in 2016, 2019, 2020 by fr33domlover <fr33domlover@riseup.net>.
|
- Written in 2016, 2019, 2020, 2022 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.
|
||||||
-
|
-
|
||||||
|
@ -18,9 +18,18 @@ module Vervis.Discussion
|
||||||
, MessageTreeNode (..)
|
, MessageTreeNode (..)
|
||||||
, getDiscussionTree
|
, getDiscussionTree
|
||||||
, getRepliesCollection
|
, getRepliesCollection
|
||||||
|
, NoteTopic (..)
|
||||||
|
, NoteParent (..)
|
||||||
|
, parseNoteContext
|
||||||
|
, parseNoteParent
|
||||||
|
, getLocalParentMessageId
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
|
import Control.Applicative
|
||||||
|
import Control.Monad
|
||||||
|
import Control.Monad.Trans.Class
|
||||||
|
import Control.Monad.Trans.Except
|
||||||
import Data.Graph.Inductive.Graph (mkGraph, lab')
|
import Data.Graph.Inductive.Graph (mkGraph, lab')
|
||||||
import Data.Graph.Inductive.PatriciaTree (Gr)
|
import Data.Graph.Inductive.PatriciaTree (Gr)
|
||||||
import Data.Graph.Inductive.Query.DFS (dffWith)
|
import Data.Graph.Inductive.Query.DFS (dffWith)
|
||||||
|
@ -39,7 +48,9 @@ import Web.ActivityPub
|
||||||
import Yesod.ActivityPub
|
import Yesod.ActivityPub
|
||||||
import Yesod.FedURI
|
import Yesod.FedURI
|
||||||
import Yesod.Hashids
|
import Yesod.Hashids
|
||||||
|
import Yesod.MonadSite
|
||||||
|
|
||||||
|
import Control.Monad.Trans.Except.Local
|
||||||
import Data.Tree.Local (sortForestOn)
|
import Data.Tree.Local (sortForestOn)
|
||||||
|
|
||||||
import Vervis.FedURI
|
import Vervis.FedURI
|
||||||
|
@ -47,7 +58,7 @@ import Vervis.Foundation
|
||||||
import Vervis.Model
|
import Vervis.Model
|
||||||
|
|
||||||
data MessageTreeNodeAuthor
|
data MessageTreeNodeAuthor
|
||||||
= MessageTreeNodeLocal LocalMessageId Sharer
|
= MessageTreeNodeLocal LocalMessageId PersonId
|
||||||
| MessageTreeNodeRemote Host LocalURI LocalURI (Maybe Text)
|
| MessageTreeNodeRemote Host LocalURI LocalURI (Maybe Text)
|
||||||
|
|
||||||
data MessageTreeNode = MessageTreeNode
|
data MessageTreeNode = MessageTreeNode
|
||||||
|
@ -59,12 +70,10 @@ data MessageTreeNode = MessageTreeNode
|
||||||
getMessages :: AppDB DiscussionId -> Handler [MessageTreeNode]
|
getMessages :: AppDB DiscussionId -> Handler [MessageTreeNode]
|
||||||
getMessages getdid = runDB $ do
|
getMessages getdid = runDB $ do
|
||||||
did <- getdid
|
did <- getdid
|
||||||
l <- select $ from $ \ (lm `InnerJoin` m `InnerJoin` p `InnerJoin` s) -> do
|
l <- select $ from $ \ (lm `InnerJoin` m) -> do
|
||||||
on $ p ^. PersonIdent ==. s ^. SharerId
|
|
||||||
on $ lm ^. LocalMessageAuthor ==. p ^. PersonId
|
|
||||||
on $ lm ^. LocalMessageRest ==. m ^. MessageId
|
on $ lm ^. LocalMessageRest ==. m ^. MessageId
|
||||||
where_ $ m ^. MessageRoot ==. val did
|
where_ $ m ^. MessageRoot ==. val did
|
||||||
return (m, lm ^. LocalMessageId, s)
|
return (m, lm ^. LocalMessageId, lm ^. LocalMessageAuthor)
|
||||||
r <- select $ from $ \ (rm `InnerJoin` m `InnerJoin` ra `InnerJoin` ro `InnerJoin` i `InnerJoin` ro2) -> do
|
r <- select $ from $ \ (rm `InnerJoin` m `InnerJoin` ra `InnerJoin` ro `InnerJoin` i `InnerJoin` ro2) -> do
|
||||||
on $ rm ^. RemoteMessageIdent ==. ro2 ^. RemoteObjectId
|
on $ rm ^. RemoteMessageIdent ==. ro2 ^. RemoteObjectId
|
||||||
on $ ro ^. RemoteObjectInstance ==. i ^. InstanceId
|
on $ ro ^. RemoteObjectInstance ==. i ^. InstanceId
|
||||||
|
@ -81,8 +90,8 @@ getMessages getdid = runDB $ do
|
||||||
)
|
)
|
||||||
return $ map mklocal l ++ map mkremote r
|
return $ map mklocal l ++ map mkremote r
|
||||||
where
|
where
|
||||||
mklocal (Entity mid m, Value lmid, Entity _ s) =
|
mklocal (Entity mid m, Value lmid, Value pid) =
|
||||||
MessageTreeNode mid m $ MessageTreeNodeLocal lmid s
|
MessageTreeNode mid m $ MessageTreeNodeLocal lmid pid
|
||||||
mkremote (Entity mid m, Value h, Value luMsg, Value luAuthor, Value name) =
|
mkremote (Entity mid m, Value h, Value luMsg, Value luAuthor, Value name) =
|
||||||
MessageTreeNode mid m $ MessageTreeNodeRemote h luMsg luAuthor name
|
MessageTreeNode mid m $ MessageTreeNodeRemote h luMsg luAuthor name
|
||||||
|
|
||||||
|
@ -120,7 +129,8 @@ getRepliesCollection here getDiscussionId404 = do
|
||||||
encodeRouteLocal <- getEncodeRouteLocal
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
encodeRouteHome <- getEncodeRouteHome
|
encodeRouteHome <- getEncodeRouteHome
|
||||||
encodeHid <- getEncodeKeyHashid
|
encodeHid <- getEncodeKeyHashid
|
||||||
let localUri' = localUri encodeRouteHome encodeHid
|
hashPerson <- getEncodeKeyHashid
|
||||||
|
let localUri' = localUri hashPerson encodeRouteHome encodeHid
|
||||||
replies = Collection
|
replies = Collection
|
||||||
{ collectionId = encodeRouteLocal here
|
{ collectionId = encodeRouteLocal here
|
||||||
, collectionType = CollectionTypeUnordered
|
, collectionType = CollectionTypeUnordered
|
||||||
|
@ -135,15 +145,13 @@ getRepliesCollection here getDiscussionId404 = do
|
||||||
where
|
where
|
||||||
selectLocals did =
|
selectLocals did =
|
||||||
E.select $ E.from $
|
E.select $ E.from $
|
||||||
\ (m `E.InnerJoin` lm `E.InnerJoin` p `E.InnerJoin` s) -> do
|
\ (m `E.InnerJoin` lm) -> 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.on $ m E.^. MessageId E.==. lm E.^. LocalMessageRest
|
||||||
E.where_ $
|
E.where_ $
|
||||||
m E.^. MessageRoot E.==. E.val did E.&&.
|
m E.^. MessageRoot E.==. E.val did E.&&.
|
||||||
E.isNothing (m E.^. MessageParent) E.&&.
|
E.isNothing (m E.^. MessageParent) E.&&.
|
||||||
E.isNothing (lm E.^. LocalMessageUnlinkedParent)
|
E.isNothing (lm E.^. LocalMessageUnlinkedParent)
|
||||||
return (s E.^. SharerIdent, lm E.^. LocalMessageId)
|
return (lm E.^. LocalMessageAuthor, lm E.^. LocalMessageId)
|
||||||
selectRemotes did =
|
selectRemotes did =
|
||||||
E.select $ E.from $
|
E.select $ E.from $
|
||||||
\ (m `E.InnerJoin` rm `E.InnerJoin` ro `E.InnerJoin` i) -> do
|
\ (m `E.InnerJoin` rm `E.InnerJoin` ro `E.InnerJoin` i) -> do
|
||||||
|
@ -155,6 +163,81 @@ getRepliesCollection here getDiscussionId404 = do
|
||||||
E.isNothing (m E.^. MessageParent) E.&&.
|
E.isNothing (m E.^. MessageParent) E.&&.
|
||||||
E.isNothing (rm E.^. RemoteMessageLostParent)
|
E.isNothing (rm E.^. RemoteMessageLostParent)
|
||||||
return (i E.^. InstanceHost, ro E.^. RemoteObjectIdent)
|
return (i E.^. InstanceHost, ro E.^. RemoteObjectIdent)
|
||||||
localUri encR encH (E.Value shrAuthor, E.Value lmid) =
|
localUri hashPerson encR encH (E.Value pid, E.Value lmid) =
|
||||||
encR $ MessageR shrAuthor (encH lmid)
|
encR $ PersonMessageR (hashPerson pid) (encH lmid)
|
||||||
remoteUri (E.Value h, E.Value lu) = ObjURI h lu
|
remoteUri (E.Value h, E.Value lu) = ObjURI h lu
|
||||||
|
|
||||||
|
data NoteTopic
|
||||||
|
= NoteTopicTicket DeckId TicketDeckId
|
||||||
|
| NoteTopicCloth LoomId TicketLoomId
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
parseNoteTopic (TicketR dkhid ltkhid) =
|
||||||
|
NoteTopicTicket
|
||||||
|
<$> decodeKeyHashidE dkhid "Note context invalid dkhid"
|
||||||
|
<*> decodeKeyHashidE ltkhid "Note context invalid ltkhid"
|
||||||
|
parseNoteTopic (ClothR lkhid ltkhid) =
|
||||||
|
NoteTopicCloth
|
||||||
|
<$> decodeKeyHashidE lkhid "Note context invalid lkhid"
|
||||||
|
<*> decodeKeyHashidE ltkhid "Note context invalid ltkhid"
|
||||||
|
parseNoteTopic _ = throwE "Local context isn't a ticket/cloth route"
|
||||||
|
|
||||||
|
parseNoteContext
|
||||||
|
:: (MonadSite m, SiteEnv m ~ App)
|
||||||
|
=> FedURI
|
||||||
|
-> ExceptT Text m (Either NoteTopic FedURI)
|
||||||
|
parseNoteContext uContext = do
|
||||||
|
let ObjURI hContext luContext = uContext
|
||||||
|
local <- hostIsLocal hContext
|
||||||
|
if local
|
||||||
|
then Left <$> do
|
||||||
|
route <-
|
||||||
|
fromMaybeE
|
||||||
|
(decodeRouteLocal luContext)
|
||||||
|
"Local context isn't a valid route"
|
||||||
|
parseNoteTopic route
|
||||||
|
else return $ Right uContext
|
||||||
|
|
||||||
|
data NoteParent
|
||||||
|
= NoteParentMessage PersonId LocalMessageId
|
||||||
|
| NoteParentTopic NoteTopic
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
parseNoteParent
|
||||||
|
:: (MonadSite m, SiteEnv m ~ App)
|
||||||
|
=> FedURI
|
||||||
|
-> ExceptT Text m (Either NoteParent FedURI)
|
||||||
|
parseNoteParent uParent = do
|
||||||
|
let ObjURI hParent luParent = uParent
|
||||||
|
local <- hostIsLocal hParent
|
||||||
|
if local
|
||||||
|
then Left <$> do
|
||||||
|
route <-
|
||||||
|
fromMaybeE
|
||||||
|
(decodeRouteLocal luParent)
|
||||||
|
"Local parent isn't a valid route"
|
||||||
|
(<|>)
|
||||||
|
(uncurry NoteParentMessage <$> parseNoteID route)
|
||||||
|
(NoteParentTopic <$> parseNoteTopic route)
|
||||||
|
else return $ Right uParent
|
||||||
|
where
|
||||||
|
parseNoteID (PersonMessageR pkhid lmkhid) =
|
||||||
|
(,) <$> decodeKeyHashidE pkhid
|
||||||
|
"Local parent has non-existent person hashid"
|
||||||
|
<*> decodeKeyHashidE lmkhid
|
||||||
|
"Local parent has non-existent message hashid"
|
||||||
|
parseNoteID _ = throwE "Local parent isn't a message route"
|
||||||
|
|
||||||
|
getLocalParentMessageId :: DiscussionId -> PersonId -> LocalMessageId -> ExceptT Text AppDB MessageId
|
||||||
|
getLocalParentMessageId did pid lmid = do
|
||||||
|
mp <- lift $ get pid
|
||||||
|
_ <- fromMaybeE mp "Local parent: no such pid"
|
||||||
|
mlm <- lift $ get lmid
|
||||||
|
lm <- fromMaybeE mlm "Local parent: no such lmid"
|
||||||
|
unless (localMessageAuthor lm == pid) $ throwE "Local parent: No such message, lmid mismatches pid"
|
||||||
|
|
||||||
|
let mid = localMessageRest lm
|
||||||
|
m <- lift $ getJust mid
|
||||||
|
unless (messageRoot m == did) $
|
||||||
|
throwE "Local parent belongs to a different discussion"
|
||||||
|
return mid
|
||||||
|
|
|
@ -13,11 +13,21 @@
|
||||||
- <http://creativecommons.org/publicdomain/zero/1.0/>.
|
- <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
-}
|
-}
|
||||||
|
|
||||||
|
-- These are for Barbie-related generated instances for ForwarderBy
|
||||||
|
{-# LANGUAGE DeriveAnyClass #-}
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
--{-# LANGUAGE StandaloneDeriving #-}
|
||||||
|
--{-# LANGUAGE UndecidableInstances #-}
|
||||||
|
|
||||||
module Vervis.Federation
|
module Vervis.Federation
|
||||||
( handleSharerInbox
|
(
|
||||||
, handleProjectInbox
|
{-
|
||||||
|
handlePersonInbox
|
||||||
|
, handleDeckInbox
|
||||||
|
, handleLoomInbox
|
||||||
, handleRepoInbox
|
, handleRepoInbox
|
||||||
, fixRunningDeliveries
|
-}
|
||||||
|
fixRunningDeliveries
|
||||||
, retryOutboxDelivery
|
, retryOutboxDelivery
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
@ -33,6 +43,7 @@ import Control.Monad.Trans.Maybe
|
||||||
import Control.Monad.Trans.Reader
|
import Control.Monad.Trans.Reader
|
||||||
import Crypto.Hash
|
import Crypto.Hash
|
||||||
import Data.Aeson
|
import Data.Aeson
|
||||||
|
import Data.Barbie
|
||||||
import Data.Bifunctor
|
import Data.Bifunctor
|
||||||
import Data.ByteString (ByteString)
|
import Data.ByteString (ByteString)
|
||||||
import Data.Either
|
import Data.Either
|
||||||
|
@ -50,6 +61,7 @@ import Data.Traversable
|
||||||
import Data.Tuple
|
import Data.Tuple
|
||||||
import Database.Persist hiding (deleteBy)
|
import Database.Persist hiding (deleteBy)
|
||||||
import Database.Persist.Sql hiding (deleteBy)
|
import Database.Persist.Sql hiding (deleteBy)
|
||||||
|
import GHC.Generics
|
||||||
import Network.HTTP.Client
|
import Network.HTTP.Client
|
||||||
import Network.HTTP.Types.Header
|
import Network.HTTP.Types.Header
|
||||||
import Network.HTTP.Types.URI
|
import Network.HTTP.Types.URI
|
||||||
|
@ -95,45 +107,22 @@ 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.Delivery
|
||||||
import Vervis.Federation.Auth
|
import Vervis.Federation.Auth
|
||||||
import Vervis.Federation.Discussion
|
|
||||||
import Vervis.Federation.Offer
|
|
||||||
import Vervis.Federation.Push
|
|
||||||
import Vervis.Federation.Ticket
|
|
||||||
import Vervis.Foundation
|
import Vervis.Foundation
|
||||||
import Vervis.Model
|
import Vervis.Model
|
||||||
import Vervis.Model.Ident
|
import Vervis.Recipient
|
||||||
import Vervis.RemoteActorStore
|
import Vervis.RemoteActorStore
|
||||||
import Vervis.Settings
|
import Vervis.Settings
|
||||||
|
|
||||||
prependError :: Monad m => Text -> ExceptT Text m a -> ExceptT Text m a
|
{-
|
||||||
prependError t a = do
|
handlePersonInbox
|
||||||
r <- lift $ runExceptT a
|
:: KeyHashid Person
|
||||||
case r of
|
|
||||||
Left e -> throwE $ t <> ": " <> e
|
|
||||||
Right x -> return x
|
|
||||||
|
|
||||||
parseTicket :: Monad m => (ShrIdent, PrjIdent) -> LocalURI -> ExceptT Text m (KeyHashid LocalTicket)
|
|
||||||
parseTicket project luContext = do
|
|
||||||
route <- case decodeRouteLocal luContext of
|
|
||||||
Nothing -> throwE "Local context isn't a valid route"
|
|
||||||
Just r -> return r
|
|
||||||
case route of
|
|
||||||
ProjectTicketR shr prj num ->
|
|
||||||
if (shr, prj) == project
|
|
||||||
then return num
|
|
||||||
else throwE "Local context ticket doesn't belong to the recipient project"
|
|
||||||
_ -> throwE "Local context isn't a ticket route"
|
|
||||||
|
|
||||||
handleSharerInbox
|
|
||||||
:: ShrIdent
|
|
||||||
-> UTCTime
|
|
||||||
-> ActivityAuthentication
|
-> ActivityAuthentication
|
||||||
-> ActivityBody
|
-> ActivityBody
|
||||||
-> ExceptT Text Handler (Text, Maybe (ExceptT Text Worker Text))
|
-> ExceptT Text Handler (Text, Maybe (ExceptT Text Worker Text))
|
||||||
handleSharerInbox shrRecip _now (ActivityAuthLocal (ActivityAuthLocalPerson pidAuthor)) body = (,Nothing) <$> do
|
handlePersonInbox recipHash (ActivityAuthLocal (LocalActorPerson pidAuthor)) body = (,Nothing) <$> do
|
||||||
(shrActivity, obiid) <- do
|
(shrActivity, obiid) <- do
|
||||||
luAct <-
|
luAct <-
|
||||||
fromMaybeE
|
fromMaybeE
|
||||||
|
@ -274,7 +263,7 @@ handleSharerInbox shrRecip now (ActivityAuthRemote author) body = do
|
||||||
localRecips <- do
|
localRecips <- do
|
||||||
mrecips <- parseAudience $ activityAudience $ actbActivity body
|
mrecips <- parseAudience $ activityAudience $ actbActivity body
|
||||||
paudLocalRecips <$> fromMaybeE mrecips "Activity with no recipients"
|
paudLocalRecips <$> fromMaybeE mrecips "Activity with no recipients"
|
||||||
msig <- checkForward $ LocalActorSharer shrRecip
|
msig <- checkForwarding $ LocalActorSharer shrRecip
|
||||||
let mfwd = (localRecips,) <$> msig
|
let mfwd = (localRecips,) <$> msig
|
||||||
case activitySpecific $ actbActivity body of
|
case activitySpecific $ actbActivity body of
|
||||||
AcceptActivity accept ->
|
AcceptActivity accept ->
|
||||||
|
@ -327,7 +316,58 @@ handleProjectInbox shrRecip prjRecip now auth body = do
|
||||||
localRecips <- do
|
localRecips <- do
|
||||||
mrecips <- parseAudience $ activityAudience $ actbActivity body
|
mrecips <- parseAudience $ activityAudience $ actbActivity body
|
||||||
paudLocalRecips <$> fromMaybeE mrecips "Activity with no recipients"
|
paudLocalRecips <$> fromMaybeE mrecips "Activity with no recipients"
|
||||||
msig <- checkForward $ LocalActorProject shrRecip prjRecip
|
msig <- checkForwarding $ LocalActorProject shrRecip prjRecip
|
||||||
|
let mfwd = (localRecips,) <$> msig
|
||||||
|
case activitySpecific $ actbActivity body of
|
||||||
|
CreateActivity (Create obj mtarget) ->
|
||||||
|
case obj of
|
||||||
|
CreateNote _ note ->
|
||||||
|
(,Nothing) <$> projectCreateNoteF now shrRecip prjRecip remoteAuthor body mfwd luActivity note
|
||||||
|
CreateTicket _ ticket ->
|
||||||
|
(,Nothing) <$> projectCreateTicketF now shrRecip prjRecip remoteAuthor body mfwd luActivity ticket mtarget
|
||||||
|
_ -> error "Unsupported create object type for projects"
|
||||||
|
FollowActivity follow ->
|
||||||
|
(,Nothing) <$> projectFollowF shrRecip prjRecip now remoteAuthor body mfwd luActivity follow
|
||||||
|
OfferActivity (Offer obj target) ->
|
||||||
|
case obj of
|
||||||
|
OfferTicket ticket ->
|
||||||
|
(,Nothing) <$> projectOfferTicketF now shrRecip prjRecip remoteAuthor body mfwd luActivity ticket target
|
||||||
|
OfferDep dep ->
|
||||||
|
projectOfferDepF now shrRecip prjRecip remoteAuthor body mfwd luActivity dep target
|
||||||
|
_ -> return ("Unsupported offer object type for projects", Nothing)
|
||||||
|
ResolveActivity resolve ->
|
||||||
|
(,Nothing) <$> projectResolveF now shrRecip prjRecip remoteAuthor body mfwd luActivity resolve
|
||||||
|
UndoActivity undo ->
|
||||||
|
(,Nothing) <$> projectUndoF shrRecip prjRecip now remoteAuthor body mfwd luActivity undo
|
||||||
|
_ -> return ("Unsupported activity type for projects", Nothing)
|
||||||
|
where
|
||||||
|
errorLocalForwarded (ActivityAuthLocalPerson pid) =
|
||||||
|
"Project inbox got local forwarded activity by pid#" <>
|
||||||
|
T.pack (show $ fromSqlKey pid)
|
||||||
|
errorLocalForwarded (ActivityAuthLocalProject jid) =
|
||||||
|
"Project inbox got local forwarded activity by jid#" <>
|
||||||
|
T.pack (show $ fromSqlKey jid)
|
||||||
|
errorLocalForwarded (ActivityAuthLocalRepo rid) =
|
||||||
|
"Project inbox got local forwarded activity by rid#" <>
|
||||||
|
T.pack (show $ fromSqlKey rid)
|
||||||
|
|
||||||
|
handleDeckInbox
|
||||||
|
:: KeyHashid Project
|
||||||
|
-> UTCTime
|
||||||
|
-> ActivityAuthentication
|
||||||
|
-> ActivityBody
|
||||||
|
-> ExceptT Text Handler (Text, Maybe (ExceptT Text Worker Text))
|
||||||
|
handleDeckInbox dkkhid now auth body = do
|
||||||
|
remoteAuthor <-
|
||||||
|
case auth of
|
||||||
|
ActivityAuthLocal local -> throwE $ errorLocalForwarded local
|
||||||
|
ActivityAuthRemote ra -> return ra
|
||||||
|
luActivity <-
|
||||||
|
fromMaybeE (activityId $ actbActivity body) "Activity without 'id'"
|
||||||
|
localRecips <- do
|
||||||
|
mrecips <- parseAudience $ activityAudience $ actbActivity body
|
||||||
|
paudLocalRecips <$> fromMaybeE mrecips "Activity with no recipients"
|
||||||
|
msig <- checkForwarding $ LocalActorProject shrRecip prjRecip
|
||||||
let mfwd = (localRecips,) <$> msig
|
let mfwd = (localRecips,) <$> msig
|
||||||
case activitySpecific $ actbActivity body of
|
case activitySpecific $ actbActivity body of
|
||||||
CreateActivity (Create obj mtarget) ->
|
CreateActivity (Create obj mtarget) ->
|
||||||
|
@ -379,7 +419,7 @@ handleRepoInbox shrRecip rpRecip now auth body = do
|
||||||
localRecips <- do
|
localRecips <- do
|
||||||
mrecips <- parseAudience $ activityAudience $ actbActivity body
|
mrecips <- parseAudience $ activityAudience $ actbActivity body
|
||||||
paudLocalRecips <$> fromMaybeE mrecips "Activity with no recipients"
|
paudLocalRecips <$> fromMaybeE mrecips "Activity with no recipients"
|
||||||
msig <- checkForward $ LocalActorRepo shrRecip rpRecip
|
msig <- checkForwarding $ LocalActorRepo shrRecip rpRecip
|
||||||
let mfwd = (localRecips,) <$> msig
|
let mfwd = (localRecips,) <$> msig
|
||||||
case activitySpecific $ actbActivity body of
|
case activitySpecific $ actbActivity body of
|
||||||
ApplyActivity (AP.Apply uObject uTarget) ->
|
ApplyActivity (AP.Apply uObject uTarget) ->
|
||||||
|
@ -420,6 +460,7 @@ handleRepoInbox shrRecip rpRecip now auth body = do
|
||||||
errorLocalForwarded (ActivityAuthLocalRepo rid) =
|
errorLocalForwarded (ActivityAuthLocalRepo rid) =
|
||||||
"Repo inbox got local forwarded activity by rid#" <>
|
"Repo inbox got local forwarded activity by rid#" <>
|
||||||
T.pack (show $ fromSqlKey rid)
|
T.pack (show $ fromSqlKey rid)
|
||||||
|
-}
|
||||||
|
|
||||||
fixRunningDeliveries :: (MonadIO m, MonadLogger m, IsSqlBackend backend) => ReaderT backend m ()
|
fixRunningDeliveries :: (MonadIO m, MonadLogger m, IsSqlBackend backend) => ReaderT backend m ()
|
||||||
fixRunningDeliveries = do
|
fixRunningDeliveries = do
|
||||||
|
@ -442,23 +483,38 @@ fixRunningDeliveries = do
|
||||||
, " forwarding deliveries"
|
, " forwarding deliveries"
|
||||||
]
|
]
|
||||||
|
|
||||||
data Fwder
|
data ForwarderBy f
|
||||||
= FwderProject ForwarderProjectId
|
= FwderPerson (f ForwarderPerson)
|
||||||
| FwderSharer ForwarderSharerId
|
| FwderGroup (f ForwarderGroup)
|
||||||
| FwderRepo ForwarderRepoId
|
| FwderRepo (f ForwarderRepo)
|
||||||
|
| FwderDeck (f ForwarderDeck)
|
||||||
|
| FwderLoom (f ForwarderLoom)
|
||||||
|
deriving (Generic, FunctorB, ConstraintsB)
|
||||||
|
|
||||||
partitionFwders :: [Fwder] -> ([ForwarderProjectId], [ForwarderSharerId], [ForwarderRepoId])
|
partitionFwders
|
||||||
partitionFwders = foldl' f ([], [], [])
|
:: [ForwarderBy f]
|
||||||
|
-> ( [f ForwarderPerson]
|
||||||
|
, [f ForwarderGroup]
|
||||||
|
, [f ForwarderRepo]
|
||||||
|
, [f ForwarderDeck]
|
||||||
|
, [f ForwarderLoom]
|
||||||
|
)
|
||||||
|
partitionFwders = foldl' f ([], [], [], [], [])
|
||||||
where
|
where
|
||||||
f (js, ss, rs) (FwderProject j) = (j : js, ss , rs)
|
f (ps, gs, rs, ds, ls) = \ fwder ->
|
||||||
f (js, ss, rs) (FwderSharer s) = (js , s : ss, rs)
|
case fwder of
|
||||||
f (js, ss, rs) (FwderRepo r) = (js , ss , r : rs)
|
FwderPerson p -> (p : ps, gs, rs, ds, ls)
|
||||||
|
FwderGroup g -> (ps, g : gs, rs, ds, ls)
|
||||||
|
FwderRepo r -> (ps, gs, r : rs, ds, ls)
|
||||||
|
FwderDeck d -> (ps, gs, rs, d : ds, ls)
|
||||||
|
FwderLoom l -> (ps, gs, rs, ds, l : ls)
|
||||||
|
|
||||||
retryOutboxDelivery :: Worker ()
|
retryOutboxDelivery :: Worker ()
|
||||||
retryOutboxDelivery = do
|
retryOutboxDelivery = do
|
||||||
logInfo "Periodic delivery starting"
|
logInfo "Periodic delivery starting"
|
||||||
now <- liftIO $ getCurrentTime
|
now <- liftIO $ getCurrentTime
|
||||||
(udls, dls, fws) <- runSiteDB $ do
|
(unlinkedHttp, linkedHttp, forwardingHttp) <- runSiteDB $ do
|
||||||
|
|
||||||
-- Get all unlinked deliveries which aren't running already in outbox
|
-- Get all unlinked deliveries which aren't running already in outbox
|
||||||
-- post handlers
|
-- post handlers
|
||||||
unlinked' <- E.select $ E.from $ \ (udl `E.InnerJoin` ob `E.InnerJoin` ura `E.InnerJoin` ro `E.InnerJoin` i `E.LeftOuterJoin` ra `E.LeftOuterJoin` rc) -> do
|
unlinked' <- E.select $ E.from $ \ (udl `E.InnerJoin` ob `E.InnerJoin` ura `E.InnerJoin` ro `E.InnerJoin` i `E.LeftOuterJoin` ra `E.LeftOuterJoin` rc) -> do
|
||||||
|
@ -483,21 +539,27 @@ retryOutboxDelivery = do
|
||||||
, ra E.?. RemoteActorId
|
, ra E.?. RemoteActorId
|
||||||
, rc E.?. RemoteCollectionId
|
, rc E.?. RemoteCollectionId
|
||||||
)
|
)
|
||||||
|
|
||||||
-- Strip the E.Value wrappers and organize the records for the
|
-- Strip the E.Value wrappers and organize the records for the
|
||||||
-- filtering and grouping we'll need to do
|
-- filtering and grouping we'll need to do
|
||||||
let unlinked = map adaptUnlinked unlinked'
|
let unlinked = map adaptUnlinked unlinked'
|
||||||
|
|
||||||
-- Split into found (recipient has been reached) and lonely (recipient
|
-- Split into found (recipient has been reached) and lonely (recipient
|
||||||
-- hasn't been reached
|
-- hasn't been reached
|
||||||
(found, lonely) = partitionMaybes unlinked
|
(found, lonely) = partitionMaybes unlinked
|
||||||
|
|
||||||
-- Turn the found ones into linked deliveries
|
-- Turn the found ones into linked deliveries
|
||||||
deleteWhere [UnlinkedDeliveryId <-. map (unlinkedID . snd) found]
|
deleteWhere [UnlinkedDeliveryId <-. map (unlinkedID . snd) found]
|
||||||
insertMany_ $ mapMaybe toLinked found
|
insertMany_ $ mapMaybe toLinked found
|
||||||
|
|
||||||
-- We're left with the lonely ones. We'll check which actors have been
|
-- We're left with the lonely ones. We'll check which actors have been
|
||||||
-- unreachable for too long, and we'll delete deliveries for them. The
|
-- unreachable for too long, and we'll delete deliveries for them. The
|
||||||
-- rest of the actors we'll try to reach by HTTP.
|
-- rest of the actors we'll try to reach by HTTP.
|
||||||
dropAfter <- lift $ asksSite $ appDropDeliveryAfter . appSettings
|
dropAfter <- lift $ asksSite $ appDropDeliveryAfter . appSettings
|
||||||
let (lonelyOld, lonelyNew) = partitionEithers $ map (decideBySinceUDL dropAfter now) lonely
|
let (lonelyOld, lonelyNew) =
|
||||||
|
partitionEithers $ map (decideBySinceUDL dropAfter now) lonely
|
||||||
deleteWhere [UnlinkedDeliveryId <-. lonelyOld]
|
deleteWhere [UnlinkedDeliveryId <-. lonelyOld]
|
||||||
|
|
||||||
-- Now let's grab the linked deliveries, and similarly delete old ones
|
-- Now let's grab the linked deliveries, and similarly delete old ones
|
||||||
-- and return the rest for HTTP delivery.
|
-- and return the rest for HTTP delivery.
|
||||||
linked <- E.select $ E.from $ \ (dl `E.InnerJoin` ra `E.InnerJoin` ro `E.InnerJoin` i `E.InnerJoin` ob) -> do
|
linked <- E.select $ E.from $ \ (dl `E.InnerJoin` ra `E.InnerJoin` ro `E.InnerJoin` i `E.InnerJoin` ob) -> do
|
||||||
|
@ -518,73 +580,68 @@ retryOutboxDelivery = do
|
||||||
, dl E.^. DeliveryForwarding
|
, dl E.^. DeliveryForwarding
|
||||||
, ob E.^. OutboxItemActivity
|
, ob E.^. OutboxItemActivity
|
||||||
)
|
)
|
||||||
let (linkedOld, linkedNew) = partitionEithers $ map (decideBySinceDL dropAfter now . adaptLinked) linked
|
let (linkedOld, linkedNew) =
|
||||||
|
partitionEithers $
|
||||||
|
map (decideBySinceDL dropAfter now . adaptLinked) linked
|
||||||
deleteWhere [DeliveryId <-. linkedOld]
|
deleteWhere [DeliveryId <-. linkedOld]
|
||||||
|
|
||||||
-- Same for forwarding deliveries, which are always linked
|
-- Same for forwarding deliveries, which are always linked
|
||||||
forwarding <- E.select $ E.from $ \ (fw `E.InnerJoin` ra `E.InnerJoin` ro `E.InnerJoin` i `E.LeftOuterJoin` (fwj `E.InnerJoin` j `E.InnerJoin` s) `E.LeftOuterJoin` (fws `E.InnerJoin` s2) `E.LeftOuterJoin` (fwr `E.InnerJoin` r `E.InnerJoin` s3)) -> do
|
forwarding <- E.select $ E.from $
|
||||||
E.on $ r E.?. RepoSharer E.==. s3 E.?. SharerId
|
\ (fw `E.InnerJoin` ra `E.InnerJoin` ro `E.InnerJoin` i
|
||||||
E.on $ fwr E.?. ForwarderRepoSender E.==. r E.?. RepoId
|
`E.LeftOuterJoin` fwp
|
||||||
|
`E.LeftOuterJoin` fwg
|
||||||
|
`E.LeftOuterJoin` fwr
|
||||||
|
`E.LeftOuterJoin` fwd
|
||||||
|
`E.LeftOuterJoin` fwl
|
||||||
|
) -> do
|
||||||
|
E.on $ E.just (fw E.^. ForwardingId) E.==. fwl E.?. ForwarderLoomTask
|
||||||
|
E.on $ E.just (fw E.^. ForwardingId) E.==. fwd E.?. ForwarderDeckTask
|
||||||
E.on $ E.just (fw E.^. ForwardingId) E.==. fwr E.?. ForwarderRepoTask
|
E.on $ E.just (fw E.^. ForwardingId) E.==. fwr E.?. ForwarderRepoTask
|
||||||
|
E.on $ E.just (fw E.^. ForwardingId) E.==. fwg E.?. ForwarderGroupTask
|
||||||
E.on $ fws E.?. ForwarderSharerSender E.==. s2 E.?. SharerId
|
E.on $ E.just (fw E.^. ForwardingId) E.==. fwp E.?. ForwarderPersonTask
|
||||||
E.on $ E.just (fw E.^. ForwardingId) E.==. fws E.?. ForwarderSharerTask
|
|
||||||
|
|
||||||
E.on $ j E.?. ProjectSharer E.==. s E.?. SharerId
|
|
||||||
E.on $ fwj E.?. ForwarderProjectSender E.==. j E.?. ProjectId
|
|
||||||
E.on $ E.just (fw E.^. ForwardingId) E.==. fwj E.?. ForwarderProjectTask
|
|
||||||
|
|
||||||
E.on $ ro E.^. RemoteObjectInstance E.==. i E.^. InstanceId
|
E.on $ ro E.^. RemoteObjectInstance E.==. i E.^. InstanceId
|
||||||
E.on $ ra E.^. RemoteActorIdent E.==. ro E.^. RemoteObjectId
|
E.on $ ra E.^. RemoteActorIdent E.==. ro E.^. RemoteObjectId
|
||||||
E.on $ fw E.^. ForwardingRecipient E.==. ra E.^. RemoteActorId
|
E.on $ fw E.^. ForwardingRecipient E.==. ra E.^. RemoteActorId
|
||||||
E.where_ $ fw E.^. ForwardingRunning E.==. E.val False
|
E.where_ $ fw E.^. ForwardingRunning E.==. E.val False
|
||||||
E.orderBy [E.asc $ ro E.^. RemoteObjectInstance, E.asc $ ra E.^. RemoteActorId]
|
E.orderBy [E.asc $ ro E.^. RemoteObjectInstance, E.asc $ ra E.^. RemoteActorId]
|
||||||
return
|
return (i, ra, fw, fwp, fwg, fwr, fwd, fwl)
|
||||||
( i E.^. InstanceId
|
let (forwardingOld, forwardingNew) =
|
||||||
, i E.^. InstanceHost
|
partitionEithers $
|
||||||
, ra E.^. RemoteActorId
|
map (decideBySinceFW dropAfter now . adaptForwarding)
|
||||||
, ra E.^. RemoteActorInbox
|
forwarding
|
||||||
, ra E.^. RemoteActorErrorSince
|
|
||||||
, fw E.^. ForwardingId
|
|
||||||
, fw E.^. ForwardingActivityRaw
|
|
||||||
|
|
||||||
, fwj E.?. ForwarderProjectId
|
|
||||||
, s E.?. SharerIdent
|
|
||||||
, j E.?. ProjectIdent
|
|
||||||
|
|
||||||
, fws E.?. ForwarderSharerId
|
|
||||||
, s2 E.?. SharerIdent
|
|
||||||
|
|
||||||
, fwr E.?. ForwarderRepoId
|
|
||||||
, s3 E.?. SharerIdent
|
|
||||||
, r E.?. RepoIdent
|
|
||||||
|
|
||||||
, fw E.^. ForwardingSignature
|
|
||||||
)
|
|
||||||
let (forwardingOld, forwardingNew) = partitionEithers $ map (decideBySinceFW dropAfter now . adaptForwarding) forwarding
|
|
||||||
(fwidsOld, fwdersOld) = unzip forwardingOld
|
(fwidsOld, fwdersOld) = unzip forwardingOld
|
||||||
(fwjidsOld, fwsidsOld, fwridsOld) = partitionFwders fwdersOld
|
(fwpidsOld, fwgidsOld, fwridsOld, fwdidsOld, fwlidsOld) =
|
||||||
deleteWhere [ForwarderProjectId <-. fwjidsOld]
|
partitionFwders fwdersOld
|
||||||
deleteWhere [ForwarderSharerId <-. fwsidsOld]
|
deleteWhere [ForwarderPersonId <-. fwpidsOld]
|
||||||
|
deleteWhere [ForwarderGroupId <-. fwgidsOld]
|
||||||
deleteWhere [ForwarderRepoId <-. fwridsOld]
|
deleteWhere [ForwarderRepoId <-. fwridsOld]
|
||||||
|
deleteWhere [ForwarderDeckId <-. fwdidsOld]
|
||||||
|
deleteWhere [ForwarderLoomId <-. fwlidsOld]
|
||||||
deleteWhere [ForwardingId <-. fwidsOld]
|
deleteWhere [ForwardingId <-. fwidsOld]
|
||||||
return (groupUnlinked lonelyNew, groupLinked linkedNew, groupForwarding forwardingNew)
|
|
||||||
|
return
|
||||||
|
( groupUnlinked lonelyNew
|
||||||
|
, groupLinked linkedNew
|
||||||
|
, groupForwarding forwardingNew
|
||||||
|
)
|
||||||
|
|
||||||
let deliver = deliverHttpBL
|
let deliver = deliverHttpBL
|
||||||
logInfo "Periodic delivery prepared DB, starting async HTTP POSTs"
|
logInfo "Periodic delivery prepared DB, starting async HTTP POSTs"
|
||||||
|
|
||||||
logDebug $
|
logDebug $
|
||||||
"Periodic delivery forking linked " <>
|
"Periodic delivery forking linked " <>
|
||||||
T.pack (show $ map (renderAuthority . snd . fst) dls)
|
T.pack (show $ map (renderAuthority . snd . fst) linkedHttp)
|
||||||
waitsDL <- traverse (fork . deliverLinked deliver now) dls
|
waitsDL <- traverse (fork . deliverLinked deliver now) linkedHttp
|
||||||
|
|
||||||
logDebug $
|
logDebug $
|
||||||
"Periodic delivery forking forwarding " <>
|
"Periodic delivery forking forwarding " <>
|
||||||
T.pack (show $ map (renderAuthority . snd . fst) fws)
|
T.pack (show $ map (renderAuthority . snd . fst) forwardingHttp)
|
||||||
waitsFW <- traverse (fork . deliverForwarding now) fws
|
waitsFW <- traverse (fork . deliverForwarding now) forwardingHttp
|
||||||
|
|
||||||
logDebug $
|
logDebug $
|
||||||
"Periodic delivery forking unlinked " <>
|
"Periodic delivery forking unlinked " <>
|
||||||
T.pack (show $ map (renderAuthority . snd . fst) udls)
|
T.pack (show $ map (renderAuthority . snd . fst) unlinkedHttp)
|
||||||
waitsUDL <- traverse (fork . deliverUnlinked deliver now) udls
|
waitsUDL <- traverse (fork . deliverUnlinked deliver now) unlinkedHttp
|
||||||
|
|
||||||
logDebug $
|
logDebug $
|
||||||
T.concat
|
T.concat
|
||||||
|
@ -621,10 +678,14 @@ retryOutboxDelivery = do
|
||||||
, since
|
, since
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
unlinkedID ((_, (_, (udlid, _, _, _))), _) = udlid
|
unlinkedID ((_, (_, (udlid, _, _, _))), _) = udlid
|
||||||
|
|
||||||
toLinked (Left raid, ((_, (_, (_, fwd, obid, _))), _)) = Just $ Delivery raid obid fwd False
|
toLinked (Left raid, ((_, (_, (_, fwd, obid, _))), _)) = Just $ Delivery raid obid fwd False
|
||||||
toLinked (Right _ , _ ) = Nothing
|
toLinked (Right _ , _ ) = Nothing
|
||||||
|
|
||||||
relevant dropAfter now since = addUTCTime dropAfter since > now
|
relevant dropAfter now since = addUTCTime dropAfter since > now
|
||||||
|
|
||||||
decideBySinceUDL dropAfter now (udl@(_, (_, (udlid, _, _, _))), msince) =
|
decideBySinceUDL dropAfter now (udl@(_, (_, (udlid, _, _, _))), msince) =
|
||||||
case msince of
|
case msince of
|
||||||
Nothing -> Right udl
|
Nothing -> Right udl
|
||||||
|
@ -632,9 +693,7 @@ retryOutboxDelivery = do
|
||||||
if relevant dropAfter now since
|
if relevant dropAfter now since
|
||||||
then Right udl
|
then Right udl
|
||||||
else Left udlid
|
else Left udlid
|
||||||
groupUnlinked
|
|
||||||
= map (second $ groupWithExtractBy1 ((==) `on` fst) fst snd)
|
|
||||||
. groupWithExtractBy ((==) `on` fst) fst snd
|
|
||||||
adaptLinked
|
adaptLinked
|
||||||
(E.Value iid, E.Value h, E.Value raid, E.Value ident, E.Value inbox, E.Value since, E.Value dlid, E.Value fwd, E.Value act) =
|
(E.Value iid, E.Value h, E.Value raid, E.Value ident, E.Value inbox, E.Value since, E.Value dlid, E.Value fwd, E.Value act) =
|
||||||
( ( (iid, h)
|
( ( (iid, h)
|
||||||
|
@ -642,6 +701,7 @@ retryOutboxDelivery = do
|
||||||
)
|
)
|
||||||
, since
|
, since
|
||||||
)
|
)
|
||||||
|
|
||||||
decideBySinceDL dropAfter now (dl@(_, (_, (dlid, _, _))), msince) =
|
decideBySinceDL dropAfter now (dl@(_, (_, (dlid, _, _))), msince) =
|
||||||
case msince of
|
case msince of
|
||||||
Nothing -> Right dl
|
Nothing -> Right dl
|
||||||
|
@ -649,56 +709,58 @@ retryOutboxDelivery = do
|
||||||
if relevant dropAfter now since
|
if relevant dropAfter now since
|
||||||
then Right dl
|
then Right dl
|
||||||
else Left dlid
|
else Left dlid
|
||||||
groupLinked
|
|
||||||
= map (second $ groupWithExtractBy1 ((==) `on` fst) fst snd)
|
|
||||||
. groupWithExtractBy ((==) `on` fst) fst snd
|
|
||||||
adaptForwarding
|
adaptForwarding
|
||||||
( E.Value iid, E.Value h, E.Value raid, E.Value inbox, E.Value since
|
( Entity iid (Instance h)
|
||||||
, E.Value fwid, E.Value body
|
, Entity raid (RemoteActor _ _ inbox _ since)
|
||||||
, E.Value mfwjid, E.Value mprj, E.Value mshr
|
, Entity fwid (Forwarding _ _ body sig _)
|
||||||
, E.Value mfwsid, E.Value mshr2
|
, mfwp, mfwg, mfwr, mfwd, mfwl
|
||||||
, E.Value mfwrid, E.Value mrp, E.Value mshr3
|
|
||||||
, E.Value sig
|
|
||||||
) =
|
) =
|
||||||
( ( (iid, h)
|
( ( (iid, h)
|
||||||
, ( (raid, inbox)
|
, ( (raid, inbox)
|
||||||
, ( fwid
|
, ( fwid
|
||||||
, BL.fromStrict body
|
, BL.fromStrict body
|
||||||
, let project = together3 mfwjid mprj mshr
|
, case (mfwp, mfwg, mfwr, mfwd, mfwl) of
|
||||||
sharer = together2 mfwsid mshr2
|
(Nothing, Nothing, Nothing, Nothing, Nothing) ->
|
||||||
repo = together3 mfwrid mrp mshr3
|
error "Found fwid without a Forwarder* record"
|
||||||
in case (project, sharer, repo) of
|
(Just fwp, Nothing, Nothing, Nothing, Nothing) ->
|
||||||
(Just (fwjid, shr, prj), Nothing, Nothing) ->
|
FwderPerson fwp
|
||||||
(FwderProject fwjid, ProjectR shr prj)
|
(Nothing, Just fwg, Nothing, Nothing, Nothing) ->
|
||||||
(Nothing, Just (fwsid, shr), Nothing) ->
|
FwderGroup fwg
|
||||||
(FwderSharer fwsid, SharerR shr)
|
(Nothing, Nothing, Just fwr, Nothing, Nothing) ->
|
||||||
(Nothing, Nothing, Just (fwrid, shr, rp)) ->
|
FwderRepo fwr
|
||||||
(FwderRepo fwrid, RepoR shr rp)
|
(Nothing, Nothing, Nothing, Just fwd, Nothing) ->
|
||||||
_ -> error $ "Non-single fwder for fw#" ++ show fwid
|
FwderDeck fwd
|
||||||
|
(Nothing, Nothing, Nothing, Nothing, Just fwl) ->
|
||||||
|
FwderLoom fwl
|
||||||
|
_ -> error "Found fwid with multiple forwarders"
|
||||||
, sig
|
, sig
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
, since
|
, since
|
||||||
)
|
)
|
||||||
where
|
|
||||||
together2 (Just x) (Just y) = Just (x, y)
|
decideBySinceFW dropAfter now (fw@(_, (_, (fwid, _, fwder, _))), msince) =
|
||||||
together2 Nothing Nothing = Nothing
|
|
||||||
together2 _ _ = error $ "Got weird forwarder for fw#" ++ show fwid
|
|
||||||
together3 :: Maybe a -> Maybe b -> Maybe c -> Maybe (a, b, c)
|
|
||||||
together3 (Just x) (Just y) (Just z) = Just (x, y, z)
|
|
||||||
together3 Nothing Nothing Nothing = Nothing
|
|
||||||
together3 _ _ _ = error $ "Got weird forwarder for fw#" ++ show fwid
|
|
||||||
decideBySinceFW dropAfter now (fw@(_, (_, (fwid, _, (fwder, _), _))), msince) =
|
|
||||||
case msince of
|
case msince of
|
||||||
Nothing -> Right fw
|
Nothing -> Right fw
|
||||||
Just since ->
|
Just since ->
|
||||||
if relevant dropAfter now since
|
if relevant dropAfter now since
|
||||||
then Right fw
|
then Right fw
|
||||||
else Left (fwid, fwder)
|
else Left (fwid, bmap entityKey fwder)
|
||||||
|
|
||||||
|
groupUnlinked
|
||||||
|
= map (second $ groupWithExtractBy1 ((==) `on` fst) fst snd)
|
||||||
|
. groupWithExtractBy ((==) `on` fst) fst snd
|
||||||
|
|
||||||
|
groupLinked
|
||||||
|
= map (second $ groupWithExtractBy1 ((==) `on` fst) fst snd)
|
||||||
|
. groupWithExtractBy ((==) `on` fst) fst snd
|
||||||
|
|
||||||
groupForwarding
|
groupForwarding
|
||||||
= map (second $ groupWithExtractBy1 ((==) `on` fst) fst snd)
|
= map (second $ groupWithExtractBy1 ((==) `on` fst) fst snd)
|
||||||
. groupWithExtractBy ((==) `on` fst) fst snd
|
. groupWithExtractBy ((==) `on` fst) fst snd
|
||||||
|
|
||||||
fork action = do
|
fork action = do
|
||||||
wait <- asyncWorker action
|
wait <- asyncWorker action
|
||||||
return $ do
|
return $ do
|
||||||
|
@ -708,6 +770,7 @@ retryOutboxDelivery = do
|
||||||
logError $ "Periodic delivery error! " <> T.pack (displayException e)
|
logError $ "Periodic delivery error! " <> T.pack (displayException e)
|
||||||
return False
|
return False
|
||||||
Right success -> return success
|
Right success -> return success
|
||||||
|
|
||||||
deliverLinked deliver now ((_, h), recips) = do
|
deliverLinked deliver now ((_, h), recips) = do
|
||||||
logDebug $ "Periodic deliver starting linked for host " <> renderAuthority h
|
logDebug $ "Periodic deliver starting linked for host " <> renderAuthority h
|
||||||
waitsR <- for recips $ \ ((raid, (ident, inbox)), delivs) -> fork $ do
|
waitsR <- for recips $ \ ((raid, (ident, inbox)), delivs) -> fork $ do
|
||||||
|
@ -740,6 +803,7 @@ retryOutboxDelivery = do
|
||||||
unless (and results) $
|
unless (and results) $
|
||||||
logError $ "Periodic DL delivery error for host " <> renderAuthority h
|
logError $ "Periodic DL delivery error for host " <> renderAuthority h
|
||||||
return True
|
return True
|
||||||
|
|
||||||
deliverUnlinked deliver now ((iid, h), recips) = do
|
deliverUnlinked deliver now ((iid, h), recips) = do
|
||||||
logDebug $ "Periodic deliver starting unlinked for host " <> renderAuthority h
|
logDebug $ "Periodic deliver starting unlinked for host " <> renderAuthority h
|
||||||
waitsR <- for recips $ \ ((uraid, luRecip), delivs) -> fork $ do
|
waitsR <- for recips $ \ ((uraid, luRecip), delivs) -> fork $ do
|
||||||
|
@ -777,22 +841,27 @@ retryOutboxDelivery = do
|
||||||
unless (and results) $
|
unless (and results) $
|
||||||
logError $ "Periodic UDL delivery error for host " <> renderAuthority h
|
logError $ "Periodic UDL delivery error for host " <> renderAuthority h
|
||||||
return True
|
return True
|
||||||
|
|
||||||
deliverForwarding now ((_, h), recips) = do
|
deliverForwarding now ((_, h), recips) = do
|
||||||
logDebug $ "Periodic deliver starting forwarding for host " <> renderAuthority h
|
logDebug $ "Periodic deliver starting forwarding for host " <> renderAuthority h
|
||||||
waitsR <- for recips $ \ ((raid, inbox), delivs) -> fork $ do
|
waitsR <- for recips $ \ ((raid, inbox), delivs) -> fork $ do
|
||||||
logDebug $
|
logDebug $
|
||||||
"Periodic deliver starting forwarding for inbox " <>
|
"Periodic deliver starting forwarding for inbox " <>
|
||||||
renderObjURI (ObjURI h inbox)
|
renderObjURI (ObjURI h inbox)
|
||||||
waitsD <- for delivs $ \ (fwid, body, (fwder, sender), sig) -> fork $ do
|
waitsD <- for delivs $ \ (fwid, body, fwderE, sig) -> fork $ do
|
||||||
|
let (fwderK, senderK) = splitForwarder fwderE
|
||||||
|
sender <- renderLocalActor <$> hashLocalActor senderK
|
||||||
e <- forwardActivity (ObjURI h inbox) sig sender body
|
e <- forwardActivity (ObjURI h inbox) sig sender body
|
||||||
case e of
|
case e of
|
||||||
Left _err -> return False
|
Left _err -> return False
|
||||||
Right _resp -> do
|
Right _resp -> do
|
||||||
runSiteDB $ do
|
runSiteDB $ do
|
||||||
case fwder of
|
case fwderK of
|
||||||
FwderProject k -> delete k
|
FwderPerson k -> delete k
|
||||||
FwderSharer k -> delete k
|
FwderGroup k -> delete k
|
||||||
FwderRepo k -> delete k
|
FwderRepo k -> delete k
|
||||||
|
FwderDeck k -> delete k
|
||||||
|
FwderLoom k -> delete k
|
||||||
delete fwid
|
delete fwid
|
||||||
return True
|
return True
|
||||||
results <- sequence waitsD
|
results <- sequence waitsD
|
||||||
|
@ -807,3 +876,14 @@ retryOutboxDelivery = do
|
||||||
unless (and results) $
|
unless (and results) $
|
||||||
logError $ "Periodic FW delivery error for host " <> renderAuthority h
|
logError $ "Periodic FW delivery error for host " <> renderAuthority h
|
||||||
return True
|
return True
|
||||||
|
where
|
||||||
|
splitForwarder (FwderPerson (Entity f (ForwarderPerson _ p))) =
|
||||||
|
(FwderPerson f, LocalActorPerson p)
|
||||||
|
splitForwarder (FwderGroup (Entity f (ForwarderGroup _ g))) =
|
||||||
|
(FwderGroup f, LocalActorGroup g)
|
||||||
|
splitForwarder (FwderRepo (Entity f (ForwarderRepo _ r))) =
|
||||||
|
(FwderRepo f, LocalActorRepo r)
|
||||||
|
splitForwarder (FwderDeck (Entity f (ForwarderDeck _ d))) =
|
||||||
|
(FwderDeck f, LocalActorDeck d)
|
||||||
|
splitForwarder (FwderLoom (Entity f (ForwarderLoom _ l))) =
|
||||||
|
(FwderLoom f, LocalActorLoom l)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{- This file is part of Vervis.
|
{- This file is part of Vervis.
|
||||||
-
|
-
|
||||||
- Written in 2019 by fr33domlover <fr33domlover@riseup.net>.
|
- Written in 2019, 2022 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.
|
||||||
-
|
-
|
||||||
|
@ -15,10 +15,10 @@
|
||||||
|
|
||||||
module Vervis.Federation.Auth
|
module Vervis.Federation.Auth
|
||||||
( RemoteAuthor (..)
|
( RemoteAuthor (..)
|
||||||
, ActivityAuthenticationLocal (..)
|
|
||||||
, ActivityAuthentication (..)
|
, ActivityAuthentication (..)
|
||||||
, ActivityBody (..)
|
, ActivityBody (..)
|
||||||
, authenticateActivity
|
, authenticateActivity
|
||||||
|
, checkForwarding
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
|
@ -94,12 +94,12 @@ 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.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.Recipient
|
||||||
import Vervis.RemoteActorStore
|
import Vervis.RemoteActorStore
|
||||||
import Vervis.Settings
|
import Vervis.Settings
|
||||||
|
|
||||||
|
@ -109,13 +109,8 @@ data RemoteAuthor = RemoteAuthor
|
||||||
, remoteAuthorId :: RemoteActorId
|
, remoteAuthorId :: RemoteActorId
|
||||||
}
|
}
|
||||||
|
|
||||||
data ActivityAuthenticationLocal
|
|
||||||
= ActivityAuthLocalPerson PersonId
|
|
||||||
| ActivityAuthLocalProject ProjectId
|
|
||||||
| ActivityAuthLocalRepo RepoId
|
|
||||||
|
|
||||||
data ActivityAuthentication
|
data ActivityAuthentication
|
||||||
= ActivityAuthLocal ActivityAuthenticationLocal
|
= ActivityAuthLocal (LocalActorBy Key)
|
||||||
| ActivityAuthRemote RemoteAuthor
|
| ActivityAuthRemote RemoteAuthor
|
||||||
|
|
||||||
data ActivityBody = ActivityBody
|
data ActivityBody = ActivityBody
|
||||||
|
@ -271,7 +266,7 @@ verifySelfSig
|
||||||
-> LocalRefURI
|
-> LocalRefURI
|
||||||
-> ByteString
|
-> ByteString
|
||||||
-> Signature
|
-> Signature
|
||||||
-> ExceptT String Handler ActivityAuthenticationLocal
|
-> ExceptT String Handler (LocalActorBy Key)
|
||||||
verifySelfSig luAuthor (LocalRefURI lruKey) input (Signature sig) = do
|
verifySelfSig luAuthor (LocalRefURI lruKey) input (Signature sig) = do
|
||||||
author <- do
|
author <- do
|
||||||
route <-
|
route <-
|
||||||
|
@ -299,22 +294,25 @@ verifySelfSig luAuthor (LocalRefURI lruKey) input (Signature sig) = do
|
||||||
ExceptT . pure $ verifySignature (actorKeyPublicBin akey) input sig
|
ExceptT . pure $ verifySignature (actorKeyPublicBin akey) input sig
|
||||||
unless valid $
|
unless valid $
|
||||||
throwE "Self sig verification says not valid"
|
throwE "Self sig verification says not valid"
|
||||||
ExceptT $ runDB $ do
|
localAuth <- unhashLocalActorE author "No such actor"
|
||||||
mauthorId <- runMaybeT $ getLocalActor author
|
withExceptT T.unpack $ runDBExcept $ findLocalAuthInDB localAuth
|
||||||
return $
|
return localAuth
|
||||||
case mauthorId of
|
|
||||||
Nothing -> Left "Local author: No such user/project"
|
|
||||||
Just id_ -> Right id_
|
|
||||||
where
|
where
|
||||||
getLocalActor (LocalActorSharer shr) = do
|
findLocalAuthInDB (LocalActorPerson pid) = do
|
||||||
sid <- MaybeT $ getKeyBy $ UniqueSharer shr
|
mp <- lift $ get pid
|
||||||
ActivityAuthLocalPerson <$> MaybeT (getKeyBy $ UniquePersonIdent sid)
|
when (isNothing mp) $ throwE "No such person"
|
||||||
getLocalActor (LocalActorProject shr prj) = do
|
findLocalAuthInDB (LocalActorGroup gid) = do
|
||||||
sid <- MaybeT $ getKeyBy $ UniqueSharer shr
|
mg <- lift $ get gid
|
||||||
ActivityAuthLocalProject <$> MaybeT (getKeyBy $ UniqueProject prj sid)
|
when (isNothing mg) $ throwE "No such group"
|
||||||
getLocalActor (LocalActorRepo shr rp) = do
|
findLocalAuthInDB (LocalActorRepo rid) = do
|
||||||
sid <- MaybeT $ getKeyBy $ UniqueSharer shr
|
mr <- lift $ get rid
|
||||||
ActivityAuthLocalRepo <$> MaybeT (getKeyBy $ UniqueRepo rp sid)
|
when (isNothing mr) $ throwE "No such repo"
|
||||||
|
findLocalAuthInDB (LocalActorDeck did) = do
|
||||||
|
md <- lift $ get did
|
||||||
|
when (isNothing md) $ throwE "No such deck"
|
||||||
|
findLocalAuthInDB (LocalActorLoom lid) = do
|
||||||
|
ml <- lift $ get lid
|
||||||
|
when (isNothing ml) $ throwE "No such loom"
|
||||||
|
|
||||||
verifyForwardedSig
|
verifyForwardedSig
|
||||||
:: Host
|
:: Host
|
||||||
|
@ -413,3 +411,31 @@ authenticateActivity now = do
|
||||||
case parseObjURI =<< (first displayException . decodeUtf8') fwd of
|
case parseObjURI =<< (first displayException . decodeUtf8') fwd of
|
||||||
Left e -> throwE $ "ActivityPub-Forwarder isn't a valid FedURI: " <> T.pack e
|
Left e -> throwE $ "ActivityPub-Forwarder isn't a valid FedURI: " <> T.pack e
|
||||||
Right u -> return u
|
Right u -> return u
|
||||||
|
|
||||||
|
checkForwarding recip = join <$> do
|
||||||
|
let hSig = hForwardingSignature
|
||||||
|
msig <- maybeHeader hSig
|
||||||
|
for msig $ \ sig -> do
|
||||||
|
_proof <- withExceptT (T.pack . displayException) $ ExceptT $
|
||||||
|
let requires = [hDigest, hActivityPubForwarder]
|
||||||
|
in prepareToVerifyHttpSigWith hSig False requires [] Nothing
|
||||||
|
forwarder <- requireHeader hActivityPubForwarder
|
||||||
|
renderUrl <- getUrlRender
|
||||||
|
return $
|
||||||
|
if forwarder == encodeUtf8 (renderUrl $ renderLocalActor recip)
|
||||||
|
then Just sig
|
||||||
|
else Nothing
|
||||||
|
where
|
||||||
|
maybeHeader n = do
|
||||||
|
let n' = decodeUtf8 $ CI.original n
|
||||||
|
hs <- lookupHeaders n
|
||||||
|
case hs of
|
||||||
|
[] -> return Nothing
|
||||||
|
[h] -> return $ Just h
|
||||||
|
_ -> throwE $ n' <> " multiple headers found"
|
||||||
|
requireHeader n = do
|
||||||
|
let n' = decodeUtf8 $ CI.original n
|
||||||
|
mh <- maybeHeader n
|
||||||
|
case mh of
|
||||||
|
Nothing -> throwE $ n' <> " header not found"
|
||||||
|
Just h -> return h
|
||||||
|
|
|
@ -65,13 +65,13 @@ 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.Federation.Util
|
import Vervis.Federation.Util
|
||||||
import Vervis.Foundation
|
import Vervis.Foundation
|
||||||
import Vervis.Model
|
import Vervis.Model
|
||||||
import Vervis.Model.Ident
|
import Vervis.Model.Ident
|
||||||
|
import Vervis.Recipient
|
||||||
import Vervis.Settings
|
import Vervis.Settings
|
||||||
import Vervis.Ticket
|
import Vervis.Ticket
|
||||||
import Vervis.Patch
|
import Vervis.Patch
|
||||||
|
@ -209,14 +209,20 @@ updateOrphans author luNote did mid = do
|
||||||
|
|
||||||
sharerCreateNoteF
|
sharerCreateNoteF
|
||||||
:: UTCTime
|
:: UTCTime
|
||||||
-> ShrIdent
|
-> PersonId
|
||||||
-> RemoteAuthor
|
-> RemoteAuthor
|
||||||
-> ActivityBody
|
-> ActivityBody
|
||||||
-> Maybe (LocalRecipientSet, ByteString)
|
-> Maybe (LocalRecipientSet, ByteString)
|
||||||
-> LocalURI
|
-> LocalURI
|
||||||
-> Note URIMode
|
-> Note URIMode
|
||||||
-> ExceptT Text Handler Text
|
-> ExceptT Text Handler Text
|
||||||
sharerCreateNoteF now shrRecip author body mfwd luCreate note = do
|
sharerCreateNoteF now pidRecip author body mfwd luCreate note = do
|
||||||
|
error "sharerCreateF temporarily disabled"
|
||||||
|
|
||||||
|
|
||||||
|
{-
|
||||||
|
|
||||||
|
|
||||||
(luNote, published, context, mparent, source, content) <- checkNote note
|
(luNote, published, context, mparent, source, content) <- checkNote note
|
||||||
case context of
|
case context of
|
||||||
Right uContext -> runDBExcept $ do
|
Right uContext -> runDBExcept $ do
|
||||||
|
@ -338,18 +344,24 @@ sharerCreateNoteF now shrRecip author body mfwd luCreate note = do
|
||||||
did <- fromMaybeE mdid "Remote parent known, but no context RemoteDiscussion"
|
did <- fromMaybeE mdid "Remote parent known, but no context RemoteDiscussion"
|
||||||
unless (messageRoot m == did) $
|
unless (messageRoot m == did) $
|
||||||
throwE "Remote parent belongs to a different discussion"
|
throwE "Remote parent belongs to a different discussion"
|
||||||
|
-}
|
||||||
|
|
||||||
projectCreateNoteF
|
projectCreateNoteF
|
||||||
:: UTCTime
|
:: UTCTime
|
||||||
-> ShrIdent
|
-> KeyHashid Project
|
||||||
-> PrjIdent
|
|
||||||
-> RemoteAuthor
|
-> RemoteAuthor
|
||||||
-> ActivityBody
|
-> ActivityBody
|
||||||
-> Maybe (LocalRecipientSet, ByteString)
|
-> Maybe (LocalRecipientSet, ByteString)
|
||||||
-> LocalURI
|
-> LocalURI
|
||||||
-> Note URIMode
|
-> Note URIMode
|
||||||
-> ExceptT Text Handler Text
|
-> ExceptT Text Handler Text
|
||||||
projectCreateNoteF now shrRecip prjRecip author body mfwd luCreate note = do
|
projectCreateNoteF now deckRecip author body mfwd luCreate note = do
|
||||||
|
error "projectCreateNoteF temporarily disabled"
|
||||||
|
|
||||||
|
|
||||||
|
{-
|
||||||
|
|
||||||
|
|
||||||
(luNote, published, context, mparent, source, content) <- checkNote note
|
(luNote, published, context, mparent, source, content) <- checkNote note
|
||||||
case context of
|
case context of
|
||||||
Right _ -> return "Not using; context isn't local"
|
Right _ -> return "Not using; context isn't local"
|
||||||
|
@ -436,18 +448,24 @@ projectCreateNoteF now shrRecip prjRecip author body mfwd luCreate note = do
|
||||||
Entity jid j <- getBy404 $ UniqueProject prjRecip sid
|
Entity jid j <- getBy404 $ UniqueProject prjRecip sid
|
||||||
a <- getJust $ projectActor j
|
a <- getJust $ projectActor j
|
||||||
return (jid, actorInbox a)
|
return (jid, actorInbox a)
|
||||||
|
-}
|
||||||
|
|
||||||
repoCreateNoteF
|
repoCreateNoteF
|
||||||
:: UTCTime
|
:: UTCTime
|
||||||
-> ShrIdent
|
-> KeyHashid Repo
|
||||||
-> RpIdent
|
|
||||||
-> RemoteAuthor
|
-> RemoteAuthor
|
||||||
-> ActivityBody
|
-> ActivityBody
|
||||||
-> Maybe (LocalRecipientSet, ByteString)
|
-> Maybe (LocalRecipientSet, ByteString)
|
||||||
-> LocalURI
|
-> LocalURI
|
||||||
-> Note URIMode
|
-> Note URIMode
|
||||||
-> ExceptT Text Handler Text
|
-> ExceptT Text Handler Text
|
||||||
repoCreateNoteF now shrRecip rpRecip author body mfwd luCreate note = do
|
repoCreateNoteF now repoRecip author body mfwd luCreate note = do
|
||||||
|
error "repoCreateNoteF temporarily disabled"
|
||||||
|
|
||||||
|
|
||||||
|
{-
|
||||||
|
|
||||||
|
|
||||||
(luNote, published, context, mparent, source, content) <- checkNote note
|
(luNote, published, context, mparent, source, content) <- checkNote note
|
||||||
case context of
|
case context of
|
||||||
Right _ -> return "Not using; context isn't local"
|
Right _ -> return "Not using; context isn't local"
|
||||||
|
@ -535,3 +553,4 @@ repoCreateNoteF now shrRecip rpRecip author body mfwd luCreate note = do
|
||||||
sid <- getKeyBy404 $ UniqueSharer shrRecip
|
sid <- getKeyBy404 $ UniqueSharer shrRecip
|
||||||
Entity rid r <- getBy404 $ UniqueRepo rpRecip sid
|
Entity rid r <- getBy404 $ UniqueRepo rpRecip sid
|
||||||
return (rid, repoInbox r)
|
return (rid, repoInbox r)
|
||||||
|
-}
|
||||||
|
|
|
@ -89,7 +89,7 @@ import Vervis.Patch
|
||||||
import Vervis.Ticket
|
import Vervis.Ticket
|
||||||
|
|
||||||
sharerAcceptF
|
sharerAcceptF
|
||||||
:: ShrIdent
|
:: KeyHashid Person
|
||||||
-> UTCTime
|
-> UTCTime
|
||||||
-> RemoteAuthor
|
-> RemoteAuthor
|
||||||
-> ActivityBody
|
-> ActivityBody
|
||||||
|
@ -97,7 +97,12 @@ sharerAcceptF
|
||||||
-> LocalURI
|
-> LocalURI
|
||||||
-> Accept URIMode
|
-> Accept URIMode
|
||||||
-> ExceptT Text Handler Text
|
-> ExceptT Text Handler Text
|
||||||
sharerAcceptF shr now author body mfwd luAccept (Accept (ObjURI hOffer luOffer) mresult) = do
|
sharerAcceptF recipHash now author body mfwd luAccept (Accept (ObjURI hOffer luOffer) mresult) = do
|
||||||
|
error "sharerAcceptF temporarily disabled"
|
||||||
|
|
||||||
|
{-
|
||||||
|
|
||||||
|
|
||||||
mres <- lift $ runDB $ do
|
mres <- lift $ runDB $ do
|
||||||
Entity pidRecip recip <- do
|
Entity pidRecip recip <- do
|
||||||
sid <- getKeyBy404 $ UniqueSharer shr
|
sid <- getKeyBy404 $ UniqueSharer shr
|
||||||
|
@ -231,9 +236,10 @@ sharerAcceptF shr now author body mfwd luAccept (Accept (ObjURI hOffer luOffer)
|
||||||
( "Inserted remote reverse ticket dep"
|
( "Inserted remote reverse ticket dep"
|
||||||
, (,collections) <$> msig
|
, (,collections) <$> msig
|
||||||
)
|
)
|
||||||
|
-}
|
||||||
|
|
||||||
sharerRejectF
|
sharerRejectF
|
||||||
:: ShrIdent
|
:: KeyHashid Person
|
||||||
-> UTCTime
|
-> UTCTime
|
||||||
-> RemoteAuthor
|
-> RemoteAuthor
|
||||||
-> ActivityBody
|
-> ActivityBody
|
||||||
|
@ -241,7 +247,14 @@ sharerRejectF
|
||||||
-> LocalURI
|
-> LocalURI
|
||||||
-> Reject URIMode
|
-> Reject URIMode
|
||||||
-> ExceptT Text Handler Text
|
-> ExceptT Text Handler Text
|
||||||
sharerRejectF shr now author body mfwd luReject (Reject (ObjURI hOffer luOffer)) = do
|
sharerRejectF recipHash now author body mfwd luReject (Reject (ObjURI hOffer luOffer)) = do
|
||||||
|
error "sharerRejectF temporarily disabled"
|
||||||
|
|
||||||
|
|
||||||
|
{-
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
lift $ runDB $ do
|
lift $ runDB $ do
|
||||||
Entity pidRecip recip <- do
|
Entity pidRecip recip <- do
|
||||||
sid <- getKeyBy404 $ UniqueSharer shr
|
sid <- getKeyBy404 $ UniqueSharer shr
|
||||||
|
@ -277,7 +290,9 @@ sharerRejectF shr now author body mfwd luReject (Reject (ObjURI hOffer luOffer))
|
||||||
Just u -> u
|
Just u -> u
|
||||||
guard $ originalRecip == remoteAuthorURI author
|
guard $ originalRecip == remoteAuthorURI author
|
||||||
lift $ delete frrid
|
lift $ delete frrid
|
||||||
|
-}
|
||||||
|
|
||||||
|
{-
|
||||||
followF
|
followF
|
||||||
:: (Route App -> Maybe a)
|
:: (Route App -> Maybe a)
|
||||||
-> Route App
|
-> Route App
|
||||||
|
@ -402,9 +417,10 @@ followF
|
||||||
doc = accept $ Just luAct
|
doc = accept $ Just luAct
|
||||||
update obiid [OutboxItemActivity =. persistJSONObjectFromDoc doc]
|
update obiid [OutboxItemActivity =. persistJSONObjectFromDoc doc]
|
||||||
return (obiid, doc)
|
return (obiid, doc)
|
||||||
|
-}
|
||||||
|
|
||||||
sharerFollowF
|
sharerFollowF
|
||||||
:: ShrIdent
|
:: KeyHashid Person
|
||||||
-> UTCTime
|
-> UTCTime
|
||||||
-> RemoteAuthor
|
-> RemoteAuthor
|
||||||
-> ActivityBody
|
-> ActivityBody
|
||||||
|
@ -412,7 +428,13 @@ sharerFollowF
|
||||||
-> LocalURI
|
-> LocalURI
|
||||||
-> AP.Follow URIMode
|
-> AP.Follow URIMode
|
||||||
-> ExceptT Text Handler Text
|
-> ExceptT Text Handler Text
|
||||||
sharerFollowF shr =
|
sharerFollowF recipHash =
|
||||||
|
error "sharerFollowF temporarily disabled"
|
||||||
|
|
||||||
|
|
||||||
|
{-
|
||||||
|
|
||||||
|
|
||||||
followF
|
followF
|
||||||
objRoute
|
objRoute
|
||||||
(SharerR shr)
|
(SharerR shr)
|
||||||
|
@ -450,10 +472,10 @@ sharerFollowF shr =
|
||||||
|
|
||||||
followers (p, Nothing) = personFollowers p
|
followers (p, Nothing) = personFollowers p
|
||||||
followers (_, Just lt) = localTicketFollowers lt
|
followers (_, Just lt) = localTicketFollowers lt
|
||||||
|
-}
|
||||||
|
|
||||||
projectFollowF
|
projectFollowF
|
||||||
:: ShrIdent
|
:: KeyHashid Project
|
||||||
-> PrjIdent
|
|
||||||
-> UTCTime
|
-> UTCTime
|
||||||
-> RemoteAuthor
|
-> RemoteAuthor
|
||||||
-> ActivityBody
|
-> ActivityBody
|
||||||
|
@ -461,7 +483,12 @@ projectFollowF
|
||||||
-> LocalURI
|
-> LocalURI
|
||||||
-> AP.Follow URIMode
|
-> AP.Follow URIMode
|
||||||
-> ExceptT Text Handler Text
|
-> ExceptT Text Handler Text
|
||||||
projectFollowF shr prj =
|
projectFollowF deckHash =
|
||||||
|
error "projectFollowF temporarily disabled"
|
||||||
|
|
||||||
|
{-
|
||||||
|
|
||||||
|
|
||||||
followF
|
followF
|
||||||
objRoute
|
objRoute
|
||||||
(ProjectR shr prj)
|
(ProjectR shr prj)
|
||||||
|
@ -493,10 +520,10 @@ projectFollowF shr prj =
|
||||||
|
|
||||||
followers (a, Nothing) = actorFollowers a
|
followers (a, Nothing) = actorFollowers a
|
||||||
followers (_, Just lt) = localTicketFollowers lt
|
followers (_, Just lt) = localTicketFollowers lt
|
||||||
|
-}
|
||||||
|
|
||||||
repoFollowF
|
repoFollowF
|
||||||
:: ShrIdent
|
:: KeyHashid Repo
|
||||||
-> RpIdent
|
|
||||||
-> UTCTime
|
-> UTCTime
|
||||||
-> RemoteAuthor
|
-> RemoteAuthor
|
||||||
-> ActivityBody
|
-> ActivityBody
|
||||||
|
@ -504,7 +531,13 @@ repoFollowF
|
||||||
-> LocalURI
|
-> LocalURI
|
||||||
-> AP.Follow URIMode
|
-> AP.Follow URIMode
|
||||||
-> ExceptT Text Handler Text
|
-> ExceptT Text Handler Text
|
||||||
repoFollowF shr rp =
|
repoFollowF repoHash =
|
||||||
|
error "repoFollowF temporarily disabled"
|
||||||
|
|
||||||
|
|
||||||
|
{-
|
||||||
|
|
||||||
|
|
||||||
followF
|
followF
|
||||||
objRoute
|
objRoute
|
||||||
(RepoR shr rp)
|
(RepoR shr rp)
|
||||||
|
@ -535,6 +568,7 @@ repoFollowF shr rp =
|
||||||
|
|
||||||
followers (r, Nothing) = repoFollowers r
|
followers (r, Nothing) = repoFollowers r
|
||||||
followers (_, Just lt) = localTicketFollowers lt
|
followers (_, Just lt) = localTicketFollowers lt
|
||||||
|
-}
|
||||||
|
|
||||||
getFollow (Left _) = return Nothing
|
getFollow (Left _) = return Nothing
|
||||||
getFollow (Right ractid) = getBy $ UniqueRemoteFollowFollow ractid
|
getFollow (Right ractid) = getBy $ UniqueRemoteFollowFollow ractid
|
||||||
|
@ -612,7 +646,7 @@ insertAcceptOnUndo actor author luUndo obiid auds = do
|
||||||
actorOutboxItem (LocalActorRepo shr rp) = RepoOutboxItemR shr rp
|
actorOutboxItem (LocalActorRepo shr rp) = RepoOutboxItemR shr rp
|
||||||
|
|
||||||
sharerUndoF
|
sharerUndoF
|
||||||
:: ShrIdent
|
:: KeyHashid Person
|
||||||
-> UTCTime
|
-> UTCTime
|
||||||
-> RemoteAuthor
|
-> RemoteAuthor
|
||||||
-> ActivityBody
|
-> ActivityBody
|
||||||
|
@ -620,7 +654,13 @@ sharerUndoF
|
||||||
-> LocalURI
|
-> LocalURI
|
||||||
-> Undo URIMode
|
-> Undo URIMode
|
||||||
-> ExceptT Text Handler Text
|
-> ExceptT Text Handler Text
|
||||||
sharerUndoF shrRecip now author body mfwd luUndo (Undo uObj) = do
|
sharerUndoF recipHash now author body mfwd luUndo (Undo uObj) = do
|
||||||
|
error "sharerUndoF temporarily disabled"
|
||||||
|
|
||||||
|
|
||||||
|
{-
|
||||||
|
|
||||||
|
|
||||||
object <- parseActivity uObj
|
object <- parseActivity uObj
|
||||||
mmmhttp <- runDBExcept $ do
|
mmmhttp <- runDBExcept $ do
|
||||||
p <- lift $ do
|
p <- lift $ do
|
||||||
|
@ -702,10 +742,10 @@ sharerUndoF shrRecip now author body mfwd luUndo (Undo uObj) = do
|
||||||
audTicket =
|
audTicket =
|
||||||
AudLocal [] [ticketFollowers]
|
AudLocal [] [ticketFollowers]
|
||||||
return ([ticketFollowers], [audAuthor, audTicket])
|
return ([ticketFollowers], [audAuthor, audTicket])
|
||||||
|
-}
|
||||||
|
|
||||||
projectUndoF
|
projectUndoF
|
||||||
:: ShrIdent
|
:: KeyHashid Project
|
||||||
-> PrjIdent
|
|
||||||
-> UTCTime
|
-> UTCTime
|
||||||
-> RemoteAuthor
|
-> RemoteAuthor
|
||||||
-> ActivityBody
|
-> ActivityBody
|
||||||
|
@ -713,7 +753,17 @@ projectUndoF
|
||||||
-> LocalURI
|
-> LocalURI
|
||||||
-> Undo URIMode
|
-> Undo URIMode
|
||||||
-> ExceptT Text Handler Text
|
-> ExceptT Text Handler Text
|
||||||
projectUndoF shrRecip prjRecip now author body mfwd luUndo (Undo uObj) = do
|
projectUndoF recipHash now author body mfwd luUndo (Undo uObj) = do
|
||||||
|
error "projectUndoF temporarily disabled"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{-
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
object <- parseActivity uObj
|
object <- parseActivity uObj
|
||||||
mmmhttp <- runDBExcept $ do
|
mmmhttp <- runDBExcept $ do
|
||||||
(Entity jid j, a) <- lift $ do
|
(Entity jid j, a) <- lift $ do
|
||||||
|
@ -794,10 +844,10 @@ projectUndoF shrRecip prjRecip now author body mfwd luUndo (Undo uObj) = do
|
||||||
audTicket =
|
audTicket =
|
||||||
AudLocal [] [ticketFollowers]
|
AudLocal [] [ticketFollowers]
|
||||||
return ([ticketFollowers], [audAuthor, audTicket])
|
return ([ticketFollowers], [audAuthor, audTicket])
|
||||||
|
-}
|
||||||
|
|
||||||
repoUndoF
|
repoUndoF
|
||||||
:: ShrIdent
|
:: KeyHashid Repo
|
||||||
-> RpIdent
|
|
||||||
-> UTCTime
|
-> UTCTime
|
||||||
-> RemoteAuthor
|
-> RemoteAuthor
|
||||||
-> ActivityBody
|
-> ActivityBody
|
||||||
|
@ -805,7 +855,14 @@ repoUndoF
|
||||||
-> LocalURI
|
-> LocalURI
|
||||||
-> Undo URIMode
|
-> Undo URIMode
|
||||||
-> ExceptT Text Handler Text
|
-> ExceptT Text Handler Text
|
||||||
repoUndoF shrRecip rpRecip now author body mfwd luUndo (Undo uObj) = do
|
repoUndoF recipHash now author body mfwd luUndo (Undo uObj) = do
|
||||||
|
error "repoUndoF temporarily disabled"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{-
|
||||||
|
|
||||||
|
|
||||||
object <- parseActivity uObj
|
object <- parseActivity uObj
|
||||||
mmmhttp <- runDBExcept $ do
|
mmmhttp <- runDBExcept $ do
|
||||||
Entity rid r <- lift $ do
|
Entity rid r <- lift $ do
|
||||||
|
@ -885,3 +942,4 @@ repoUndoF shrRecip rpRecip now author body mfwd luUndo (Undo uObj) = do
|
||||||
audTicket =
|
audTicket =
|
||||||
AudLocal [] [ticketFollowers]
|
AudLocal [] [ticketFollowers]
|
||||||
return ([ticketFollowers], [audAuthor, audTicket])
|
return ([ticketFollowers], [audAuthor, audTicket])
|
||||||
|
-}
|
||||||
|
|
|
@ -69,7 +69,7 @@ import Vervis.Model
|
||||||
import Vervis.Model.Ident
|
import Vervis.Model.Ident
|
||||||
|
|
||||||
sharerPushF
|
sharerPushF
|
||||||
:: ShrIdent
|
:: KeyHashid Person
|
||||||
-> UTCTime
|
-> UTCTime
|
||||||
-> RemoteAuthor
|
-> RemoteAuthor
|
||||||
-> ActivityBody
|
-> ActivityBody
|
||||||
|
@ -77,7 +77,13 @@ sharerPushF
|
||||||
-> LocalURI
|
-> LocalURI
|
||||||
-> Push URIMode
|
-> Push URIMode
|
||||||
-> ExceptT Text Handler Text
|
-> ExceptT Text Handler Text
|
||||||
sharerPushF shr now author body mfwd luPush push = do
|
sharerPushF recipHash now author body mfwd luPush push = do
|
||||||
|
error "sharerPushF temporarily disabled"
|
||||||
|
|
||||||
|
|
||||||
|
{-
|
||||||
|
|
||||||
|
|
||||||
lift $ runDB $ do
|
lift $ runDB $ do
|
||||||
Entity pidRecip recip <- do
|
Entity pidRecip recip <- do
|
||||||
sid <- getKeyBy404 $ UniqueSharer shr
|
sid <- getKeyBy404 $ UniqueSharer shr
|
||||||
|
@ -113,3 +119,4 @@ sharerPushF shr now author body mfwd luPush push = do
|
||||||
delete ibiid
|
delete ibiid
|
||||||
return Nothing
|
return Nothing
|
||||||
Just _ -> return $ Just ractid
|
Just _ -> return $ Just ractid
|
||||||
|
-}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -72,12 +72,13 @@ import qualified Network.HTTP.Signature as S (Algorithm (..))
|
||||||
import Crypto.PublicVerifKey
|
import Crypto.PublicVerifKey
|
||||||
import Network.FedURI
|
import Network.FedURI
|
||||||
import Web.ActivityAccess
|
import Web.ActivityAccess
|
||||||
import Web.ActivityPub hiding (Ticket, TicketDependency, Bundle, Patch)
|
|
||||||
import Yesod.ActivityPub
|
import Yesod.ActivityPub
|
||||||
import Yesod.FedURI
|
import Yesod.FedURI
|
||||||
import Yesod.Hashids
|
import Yesod.Hashids
|
||||||
import Yesod.MonadSite
|
import Yesod.MonadSite
|
||||||
|
|
||||||
|
import qualified Web.ActivityPub as AP
|
||||||
|
|
||||||
import Text.Email.Local
|
import Text.Email.Local
|
||||||
import Text.Jasmine.Local (discardm)
|
import Text.Jasmine.Local (discardm)
|
||||||
import Yesod.Paginate.Local
|
import Yesod.Paginate.Local
|
||||||
|
@ -126,15 +127,20 @@ data App = App
|
||||||
|
|
||||||
-- Aliases for the routes file, because it doesn't like spaces in path piece
|
-- Aliases for the routes file, because it doesn't like spaces in path piece
|
||||||
-- type names.
|
-- type names.
|
||||||
|
type PersonKeyHashid = KeyHashid Person
|
||||||
|
type GroupKeyHashid = KeyHashid Group
|
||||||
|
type RepoKeyHashid = KeyHashid Repo
|
||||||
type OutboxItemKeyHashid = KeyHashid OutboxItem
|
type OutboxItemKeyHashid = KeyHashid OutboxItem
|
||||||
type SshKeyKeyHashid = KeyHashid SshKey
|
type SshKeyKeyHashid = KeyHashid SshKey
|
||||||
type MessageKeyHashid = KeyHashid Message
|
type MessageKeyHashid = KeyHashid Message
|
||||||
type LocalMessageKeyHashid = KeyHashid LocalMessage
|
type LocalMessageKeyHashid = KeyHashid LocalMessage
|
||||||
type LocalTicketKeyHashid = KeyHashid LocalTicket
|
|
||||||
type TicketAuthorLocalKeyHashid = KeyHashid TicketAuthorLocal
|
|
||||||
type TicketDepKeyHashid = KeyHashid LocalTicketDependency
|
type TicketDepKeyHashid = KeyHashid LocalTicketDependency
|
||||||
type BundleKeyHashid = KeyHashid Bundle
|
type BundleKeyHashid = KeyHashid Bundle
|
||||||
type PatchKeyHashid = KeyHashid Patch
|
type PatchKeyHashid = KeyHashid Patch
|
||||||
|
type DeckKeyHashid = KeyHashid Deck
|
||||||
|
type LoomKeyHashid = KeyHashid Loom
|
||||||
|
type TicketDeckKeyHashid = KeyHashid TicketDeck
|
||||||
|
type TicketLoomKeyHashid = KeyHashid TicketLoom
|
||||||
|
|
||||||
-- This is where we define all of the routes in our application. For a full
|
-- This is where we define all of the routes in our application. For a full
|
||||||
-- explanation of the syntax, please see:
|
-- explanation of the syntax, please see:
|
||||||
|
@ -203,13 +209,15 @@ instance Yesod App where
|
||||||
csrfCheckMiddleware
|
csrfCheckMiddleware
|
||||||
handler
|
handler
|
||||||
(getCurrentRoute >>= \ mr -> case mr of
|
(getCurrentRoute >>= \ mr -> case mr of
|
||||||
Nothing -> return False
|
Nothing -> return False
|
||||||
Just PostReceiveR -> return False
|
Just PostReceiveR -> return False
|
||||||
Just (SharerOutboxR _) -> return False
|
Just (PersonOutboxR _) -> return False
|
||||||
Just (SharerInboxR _) -> return False
|
Just (PersonInboxR _) -> return False
|
||||||
Just (ProjectInboxR _ _) -> return False
|
Just (GroupInboxR _) -> return False
|
||||||
Just (RepoInboxR _ _) -> return False
|
Just (RepoInboxR _) -> return False
|
||||||
Just (GitUploadRequestR _ _) -> return False
|
Just (DeckInboxR _) -> return False
|
||||||
|
Just (LoomInboxR _) -> return False
|
||||||
|
Just (GitUploadRequestR _) -> return False
|
||||||
Just (DvaraR _) -> return False
|
Just (DvaraR _) -> return False
|
||||||
Just r -> isWriteRequest r
|
Just r -> isWriteRequest r
|
||||||
)
|
)
|
||||||
|
@ -245,13 +253,14 @@ instance Yesod App where
|
||||||
mperson <- do
|
mperson <- do
|
||||||
mperson' <- maybeAuthAllowUnverified
|
mperson' <- maybeAuthAllowUnverified
|
||||||
for mperson' $ \ (p@(Entity pid person), verified) -> runDB $ do
|
for mperson' $ \ (p@(Entity pid person), verified) -> runDB $ do
|
||||||
sharer <- getJust $ personIdent person
|
inboxID <- actorInbox <$> getJust (personActor person)
|
||||||
unread <- do
|
unread <- do
|
||||||
vs <- countUnread $ personInbox person
|
vs <- countUnread inboxID
|
||||||
case vs :: [E.Value Int] of
|
case vs :: [E.Value Int] of
|
||||||
[E.Value i] -> return i
|
[E.Value i] -> return i
|
||||||
_ -> error $ "countUnread returned " ++ show vs
|
_ -> error $ "countUnread returned " ++ show vs
|
||||||
return (p, verified, sharer, unread)
|
hash <- encodeKeyHashid pid
|
||||||
|
return (p, hash, verified, unread)
|
||||||
(title, bcs) <- breadcrumbs
|
(title, bcs) <- breadcrumbs
|
||||||
|
|
||||||
-- We break up the default layout into two components:
|
-- We break up the default layout into two components:
|
||||||
|
@ -291,24 +300,34 @@ instance Yesod App where
|
||||||
|
|
||||||
-- Who can access which pages.
|
-- Who can access which pages.
|
||||||
isAuthorized r w = case (r, w) of
|
isAuthorized r w = case (r, w) of
|
||||||
|
|
||||||
|
-- Authentication
|
||||||
|
|
||||||
(AuthR a , True)
|
(AuthR a , True)
|
||||||
| a == resendVerifyR -> personFromResendForm
|
| a == resendVerifyR -> personFromResendForm
|
||||||
(AuthR (PluginR "account" ["verify", u, _]), False) -> personUnver u
|
(AuthR (PluginR "account" ["verify", u, _]), False) -> personUnver u
|
||||||
|
|
||||||
(PublishR , True) -> personAny
|
-- Client
|
||||||
|
|
||||||
(SharerInboxR shr , False) -> person shr
|
(NotificationsR, _ ) -> personAny
|
||||||
(NotificationsR shr , _ ) -> person shr
|
(PublishR , True) -> personAny
|
||||||
(SharerOutboxR shr , True) -> person shr
|
|
||||||
(SharerFollowR shr , True) -> personAny
|
|
||||||
(SharerUnfollowR shr , True) -> personAny
|
|
||||||
|
|
||||||
|
-- Person
|
||||||
|
|
||||||
|
(PersonInboxR p , False) -> person p
|
||||||
|
(PersonOutboxR p , True) -> person p
|
||||||
|
|
||||||
|
-- Group
|
||||||
|
|
||||||
|
{-
|
||||||
(GroupsR , True) -> personAny
|
(GroupsR , True) -> personAny
|
||||||
(GroupNewR , _ ) -> personAny
|
(GroupNewR , _ ) -> personAny
|
||||||
(GroupMembersR grp , True) -> groupAdmin grp
|
(GroupMembersR grp , True) -> groupAdmin grp
|
||||||
(GroupMemberNewR grp , _ ) -> groupAdmin grp
|
(GroupMemberNewR grp , _ ) -> groupAdmin grp
|
||||||
(GroupMemberR grp _memb , True) -> groupAdmin grp
|
(GroupMemberR grp _memb , True) -> groupAdmin grp
|
||||||
|
-}
|
||||||
|
|
||||||
|
{-
|
||||||
(KeysR , _ ) -> personAny
|
(KeysR , _ ) -> personAny
|
||||||
(KeyR _key , _ ) -> personAny
|
(KeyR _key , _ ) -> personAny
|
||||||
(KeyNewR , _ ) -> personAny
|
(KeyNewR , _ ) -> personAny
|
||||||
|
@ -320,31 +339,33 @@ instance Yesod App where
|
||||||
(ProjectRoleR shr _rl , _ ) -> personOrGroupAdmin shr
|
(ProjectRoleR shr _rl , _ ) -> personOrGroupAdmin shr
|
||||||
(ProjectRoleOpsR shr _rl , _ ) -> personOrGroupAdmin shr
|
(ProjectRoleOpsR shr _rl , _ ) -> personOrGroupAdmin shr
|
||||||
(ProjectRoleOpNewR shr _rl , _ ) -> personOrGroupAdmin shr
|
(ProjectRoleOpNewR shr _rl , _ ) -> personOrGroupAdmin shr
|
||||||
|
-}
|
||||||
|
|
||||||
|
-- Repo
|
||||||
|
|
||||||
|
(RepoInboxR _ , False) -> personAny
|
||||||
|
|
||||||
|
-- Deck
|
||||||
|
|
||||||
|
(DeckInboxR _ , False) -> personAny
|
||||||
|
|
||||||
|
-- Loom
|
||||||
|
|
||||||
|
(LoomInboxR _ , False) -> personAny
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(ReposR shr , True) -> personOrGroupAdmin shr
|
|
||||||
(RepoNewR shr , _ ) -> personOrGroupAdmin shr
|
|
||||||
(RepoR shar _ , True) -> person shar
|
|
||||||
(RepoEditR shr _rp , _ ) -> person shr
|
|
||||||
(RepoFollowR _shr _rp , True) -> personAny
|
|
||||||
(RepoUnfollowR _shr _rp , True) -> personAny
|
|
||||||
(RepoDevsR shr _rp , _ ) -> person shr
|
|
||||||
(RepoDevNewR shr _rp , _ ) -> person shr
|
|
||||||
(RepoDevR shr _rp _dev , _ ) -> person shr
|
|
||||||
|
|
||||||
(ProjectsR shr , True) -> personOrGroupAdmin shr
|
|
||||||
(ProjectNewR shr , _ ) -> personOrGroupAdmin shr
|
|
||||||
(ProjectR shr _prj , True) -> person shr
|
|
||||||
(ProjectEditR shr _prj , _ ) -> person shr
|
|
||||||
(ProjectFollowR _shr _prj , _ ) -> personAny
|
|
||||||
(ProjectUnfollowR _shr _prj , _ ) -> personAny
|
|
||||||
(ProjectDevsR shr _prj , _ ) -> person shr
|
|
||||||
(ProjectDevNewR shr _prj , _ ) -> person shr
|
|
||||||
(ProjectDevR shr _prj _dev , _ ) -> person shr
|
|
||||||
|
|
||||||
-- (GlobalWorkflowsR , _ ) -> serverAdmin
|
-- (GlobalWorkflowsR , _ ) -> serverAdmin
|
||||||
-- (GlobalWorkflowNewR , _ ) -> serverAdmin
|
-- (GlobalWorkflowNewR , _ ) -> serverAdmin
|
||||||
-- (GlobalWorkflowR _wfl , _ ) -> serverAdmin
|
-- (GlobalWorkflowR _wfl , _ ) -> serverAdmin
|
||||||
|
|
||||||
|
{-
|
||||||
(WorkflowsR shr , _ ) -> personOrGroupAdmin shr
|
(WorkflowsR shr , _ ) -> personOrGroupAdmin shr
|
||||||
(WorkflowNewR shr , _ ) -> personOrGroupAdmin shr
|
(WorkflowNewR shr , _ ) -> personOrGroupAdmin shr
|
||||||
(WorkflowR shr _wfl , _ ) -> personOrGroupAdmin shr
|
(WorkflowR shr _wfl , _ ) -> personOrGroupAdmin shr
|
||||||
|
@ -357,7 +378,9 @@ instance Yesod App where
|
||||||
(WorkflowEnumCtorsR shr _ _ , _ ) -> personOrGroupAdmin shr
|
(WorkflowEnumCtorsR shr _ _ , _ ) -> personOrGroupAdmin shr
|
||||||
(WorkflowEnumCtorNewR shr _ _ , _ ) -> personOrGroupAdmin shr
|
(WorkflowEnumCtorNewR shr _ _ , _ ) -> personOrGroupAdmin shr
|
||||||
(WorkflowEnumCtorR shr _ _ _ , _ ) -> personOrGroupAdmin shr
|
(WorkflowEnumCtorR shr _ _ _ , _ ) -> personOrGroupAdmin shr
|
||||||
|
-}
|
||||||
|
|
||||||
|
{-
|
||||||
(ProjectTicketsR s j , True) -> projOp ProjOpOpenTicket s j
|
(ProjectTicketsR s j , True) -> projOp ProjOpOpenTicket s j
|
||||||
(ProjectTicketNewR s j , _ ) -> projOp ProjOpOpenTicket s j
|
(ProjectTicketNewR s j , _ ) -> projOp ProjOpOpenTicket s j
|
||||||
(ProjectTicketR user _ _ , True) -> person user
|
(ProjectTicketR user _ _ , True) -> person user
|
||||||
|
@ -380,6 +403,8 @@ instance Yesod App where
|
||||||
(ProjectTicketDepsR s j _ , True) -> projOp ProjOpAddTicketDep s j
|
(ProjectTicketDepsR s j _ , True) -> projOp ProjOpAddTicketDep s j
|
||||||
(ProjectTicketDepNewR s j _ , _ ) -> projOp ProjOpAddTicketDep s j
|
(ProjectTicketDepNewR s j _ , _ ) -> projOp ProjOpAddTicketDep s j
|
||||||
(TicketDepOldR s j _ _ , True) -> projOp ProjOpRemoveTicketDep s j
|
(TicketDepOldR s j _ _ , True) -> projOp ProjOpRemoveTicketDep s j
|
||||||
|
-}
|
||||||
|
|
||||||
_ -> return Authorized
|
_ -> return Authorized
|
||||||
where
|
where
|
||||||
nobody :: Handler AuthResult
|
nobody :: Handler AuthResult
|
||||||
|
@ -412,11 +437,10 @@ instance Yesod App where
|
||||||
personAny :: Handler AuthResult
|
personAny :: Handler AuthResult
|
||||||
personAny = personAnd $ \ _p -> return Authorized
|
personAny = personAnd $ \ _p -> return Authorized
|
||||||
|
|
||||||
person :: ShrIdent -> Handler AuthResult
|
person :: KeyHashid Person -> Handler AuthResult
|
||||||
person ident = personAnd $ \ (Entity _ p) -> do
|
person hash = personAnd $ \ (Entity pid _) -> do
|
||||||
let sid = personIdent p
|
hash' <- encodeKeyHashid pid
|
||||||
sharer <- runDB $ getJust sid
|
return $ if hash == hash'
|
||||||
return $ if ident == sharerIdent sharer
|
|
||||||
then Authorized
|
then Authorized
|
||||||
else Unauthorized "No access to this operation"
|
else Unauthorized "No access to this operation"
|
||||||
|
|
||||||
|
@ -454,6 +478,7 @@ instance Yesod App where
|
||||||
return $
|
return $
|
||||||
Unauthorized "Requesting resend for invalid username"
|
Unauthorized "Requesting resend for invalid username"
|
||||||
|
|
||||||
|
{-
|
||||||
groupRole :: (GroupRole -> Bool) -> ShrIdent -> Handler AuthResult
|
groupRole :: (GroupRole -> Bool) -> ShrIdent -> Handler AuthResult
|
||||||
groupRole role grp = personAnd $ \ (Entity pid _p) -> runDB $ do
|
groupRole role grp = personAnd $ \ (Entity pid _p) -> runDB $ do
|
||||||
Entity sid _s <- getBy404 $ UniqueSharer grp
|
Entity sid _s <- getBy404 $ UniqueSharer grp
|
||||||
|
@ -507,6 +532,7 @@ instance Yesod App where
|
||||||
_ ->
|
_ ->
|
||||||
Unauthorized
|
Unauthorized
|
||||||
"You need a project role with that operation enabled"
|
"You need a project role with that operation enabled"
|
||||||
|
-}
|
||||||
|
|
||||||
-- This function creates static content files in the static folder
|
-- This function creates static content files in the static folder
|
||||||
-- and names them based on a hash of their content. This allows
|
-- and names them based on a hash of their content. This allows
|
||||||
|
@ -605,38 +631,42 @@ instance AccountDB AccountPersistDB' where
|
||||||
|
|
||||||
addNewUser name email key pwd = AccountPersistDB' $ runDB $ do
|
addNewUser name email key pwd = AccountPersistDB' $ runDB $ do
|
||||||
now <- liftIO getCurrentTime
|
now <- liftIO getCurrentTime
|
||||||
let sharer = Sharer
|
ibid <- insert Inbox
|
||||||
{ sharerIdent = text2shr name
|
obid <- insert Outbox
|
||||||
, sharerName = Nothing
|
fsid <- insert FollowerSet
|
||||||
, sharerCreated = now
|
let actor = Actor
|
||||||
|
{ actorName = name
|
||||||
|
, actorDesc = ""
|
||||||
|
, actorCreatedAt = now
|
||||||
|
, actorInbox = ibid
|
||||||
|
, actorOutbox = obid
|
||||||
|
, actorFollowers = fsid
|
||||||
}
|
}
|
||||||
msid <- insertBy sharer
|
aid <- insert actor
|
||||||
case msid of
|
let defTime = UTCTime (ModifiedJulianDay 0) 0
|
||||||
|
person = Person
|
||||||
|
{ personUsername = text2username $ name
|
||||||
|
, personLogin = name
|
||||||
|
, personPassphraseHash = pwd
|
||||||
|
, personEmail = email
|
||||||
|
, personVerified = False
|
||||||
|
, personVerifiedKey = key
|
||||||
|
, personVerifiedKeyCreated = now
|
||||||
|
, personResetPassKey = ""
|
||||||
|
, personResetPassKeyCreated = defTime
|
||||||
|
, personActor = aid
|
||||||
|
-- , personReviewFollow = True
|
||||||
|
}
|
||||||
|
mpid <- insertBy person
|
||||||
|
case mpid of
|
||||||
Left _ -> do
|
Left _ -> do
|
||||||
|
delete aid
|
||||||
|
delete ibid
|
||||||
|
delete obid
|
||||||
|
delete fsid
|
||||||
mr <- getMessageRender
|
mr <- getMessageRender
|
||||||
return $ Left $ mr $ MsgUsernameExists name
|
return $ Left $ mr $ MsgUsernameExists name
|
||||||
Right sid -> do
|
Right pid -> return $ Right $ Entity pid person
|
||||||
ibid <- insert Inbox
|
|
||||||
obid <- insert Outbox
|
|
||||||
fsid <- insert FollowerSet
|
|
||||||
let defTime = UTCTime (ModifiedJulianDay 0) 0
|
|
||||||
person = Person
|
|
||||||
{ personIdent = sid
|
|
||||||
, personLogin = name
|
|
||||||
, personPassphraseHash = pwd
|
|
||||||
, personEmail = email
|
|
||||||
, personVerified = False
|
|
||||||
, personVerifiedKey = key
|
|
||||||
, personVerifiedKeyCreated = now
|
|
||||||
, personResetPassKey = ""
|
|
||||||
, personResetPassKeyCreated = defTime
|
|
||||||
, personAbout = ""
|
|
||||||
, personInbox = ibid
|
|
||||||
, personOutbox = obid
|
|
||||||
, personFollowers = fsid
|
|
||||||
}
|
|
||||||
pid <- insert person
|
|
||||||
return $ Right $ Entity pid person
|
|
||||||
|
|
||||||
verifyAccount = morphAPDB . verifyAccount
|
verifyAccount = morphAPDB . verifyAccount
|
||||||
setVerifyKey = (morphAPDB .) . setVerifyKey
|
setVerifyKey = (morphAPDB .) . setVerifyKey
|
||||||
|
@ -744,7 +774,7 @@ instance YesodRemoteActorStore App where
|
||||||
instance YesodActivityPub App where
|
instance YesodActivityPub App where
|
||||||
siteInstanceHost = appInstanceHost . appSettings
|
siteInstanceHost = appInstanceHost . appSettings
|
||||||
sitePostSignedHeaders _ =
|
sitePostSignedHeaders _ =
|
||||||
hRequestTarget :| [hHost, hDate, hDigest, hActivityPubActor]
|
hRequestTarget :| [hHost, hDate, hDigest, AP.hActivityPubActor]
|
||||||
siteGetHttpSign = do
|
siteGetHttpSign = do
|
||||||
(akey1, akey2, new1) <- liftIO . readTVarIO =<< asksSite appActorKeys
|
(akey1, akey2, new1) <- liftIO . readTVarIO =<< asksSite appActorKeys
|
||||||
renderUrl <- askUrlRender
|
renderUrl <- askUrlRender
|
||||||
|
@ -759,6 +789,7 @@ instance YesodPaginate App where
|
||||||
|
|
||||||
instance YesodBreadcrumbs App where
|
instance YesodBreadcrumbs App where
|
||||||
breadcrumb route = return $ case route of
|
breadcrumb route = return $ case route of
|
||||||
|
{-
|
||||||
StaticR _ -> ("", Nothing)
|
StaticR _ -> ("", Nothing)
|
||||||
FaviconSvgR -> ("", Nothing)
|
FaviconSvgR -> ("", Nothing)
|
||||||
FaviconPngR -> ("", Nothing)
|
FaviconPngR -> ("", Nothing)
|
||||||
|
@ -985,5 +1016,6 @@ instance YesodBreadcrumbs App where
|
||||||
)
|
)
|
||||||
|
|
||||||
WikiPageR shr prj _page -> ("Wiki", Just $ ProjectR shr prj)
|
WikiPageR shr prj _page -> ("Wiki", Just $ ProjectR shr prj)
|
||||||
|
-}
|
||||||
|
|
||||||
_ -> ("PAGE TITLE HERE", Just HomeR)
|
_ -> ("PAGE TITLE HERE", Just HomeR)
|
||||||
|
|
|
@ -15,13 +15,16 @@
|
||||||
-}
|
-}
|
||||||
|
|
||||||
module Vervis.Git
|
module Vervis.Git
|
||||||
( readSourceView
|
(
|
||||||
|
{-
|
||||||
|
readSourceView
|
||||||
, readChangesView
|
, readChangesView
|
||||||
, listRefs
|
, listRefs
|
||||||
, readPatch
|
, readPatch
|
||||||
, lastCommitTime
|
, lastCommitTime
|
||||||
, writePostReceiveHooks
|
-}
|
||||||
, applyGitPatches
|
writePostReceiveHooks
|
||||||
|
--, applyGitPatches
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
|
@ -53,6 +56,7 @@ import Data.Time.Clock (UTCTime (..))
|
||||||
import Data.Time.Clock.POSIX (posixSecondsToUTCTime)
|
import Data.Time.Clock.POSIX (posixSecondsToUTCTime)
|
||||||
import Data.Traversable (for)
|
import Data.Traversable (for)
|
||||||
import Data.Word (Word32)
|
import Data.Word (Word32)
|
||||||
|
import Database.Persist
|
||||||
import System.Exit
|
import System.Exit
|
||||||
import System.Hourglass (timeCurrent)
|
import System.Hourglass (timeCurrent)
|
||||||
import System.Process.Typed
|
import System.Process.Typed
|
||||||
|
@ -73,6 +77,7 @@ import qualified Database.Esqueleto as E
|
||||||
|
|
||||||
import Network.FedURI
|
import Network.FedURI
|
||||||
import Yesod.ActivityPub
|
import Yesod.ActivityPub
|
||||||
|
import Yesod.Hashids
|
||||||
import Yesod.MonadSite
|
import Yesod.MonadSite
|
||||||
|
|
||||||
import Data.ByteString.Char8.Local (takeLine)
|
import Data.ByteString.Char8.Local (takeLine)
|
||||||
|
@ -95,6 +100,7 @@ import Vervis.Readme
|
||||||
import Vervis.Settings
|
import Vervis.Settings
|
||||||
import Vervis.SourceTree
|
import Vervis.SourceTree
|
||||||
|
|
||||||
|
{-
|
||||||
matchReadme :: (ModePerm, ObjId, Text, EntObjType) -> Bool
|
matchReadme :: (ModePerm, ObjId, Text, EntObjType) -> Bool
|
||||||
matchReadme (_, _, name, EntObjBlob) = isReadme name
|
matchReadme (_, _, name, EntObjBlob) = isReadme name
|
||||||
matchReadme _ = False
|
matchReadme _ = False
|
||||||
|
@ -340,19 +346,19 @@ lastCommitTime repo =
|
||||||
utc (Elapsed (Seconds i)) = posixSecondsToUTCTime $ fromIntegral i
|
utc (Elapsed (Seconds i)) = posixSecondsToUTCTime $ fromIntegral i
|
||||||
utc0 = UTCTime (ModifiedJulianDay 0) 0
|
utc0 = UTCTime (ModifiedJulianDay 0) 0
|
||||||
foldlM' i l f = foldlM f i l
|
foldlM' i l f = foldlM f i l
|
||||||
|
-}
|
||||||
|
|
||||||
writePostReceiveHooks :: WorkerDB ()
|
writePostReceiveHooks :: WorkerDB ()
|
||||||
writePostReceiveHooks = do
|
writePostReceiveHooks = do
|
||||||
repos <- E.select $ E.from $ \ (r `E.InnerJoin` s) -> do
|
|
||||||
E.on $ r E.^. RepoSharer E.==. s E.^. SharerId
|
|
||||||
E.where_ $ r E.^. RepoVcs E.==. E.val VCSGit
|
|
||||||
return (s E.^. SharerIdent, r E.^. RepoIdent)
|
|
||||||
hook <- asksSite $ appPostReceiveHookFile . appSettings
|
hook <- asksSite $ appPostReceiveHookFile . appSettings
|
||||||
authority <- asksSite $ renderAuthority . siteInstanceHost
|
authority <- asksSite $ renderAuthority . siteInstanceHost
|
||||||
for_ repos $ \ (E.Value shr, E.Value rp) -> do
|
repos <- selectKeysList [RepoVcs ==. VCSGit] []
|
||||||
path <- askRepoDir shr rp
|
for_ repos $ \ repoID -> do
|
||||||
liftIO $ writeHookFile path hook authority (shr2text shr) (rp2text rp)
|
repoHash <- encodeKeyHashid repoID
|
||||||
|
path <- askRepoDir repoHash
|
||||||
|
liftIO $ writeHookFile path hook authority (keyHashidText repoHash)
|
||||||
|
|
||||||
|
{-
|
||||||
applyGitPatches shr rp branch patches = do
|
applyGitPatches shr rp branch patches = do
|
||||||
path <- askRepoDir shr rp
|
path <- askRepoDir shr rp
|
||||||
let input = BL.concat $ NE.toList $ NE.map (BL.fromStrict . TE.encodeUtf8) patches
|
let input = BL.concat $ NE.toList $ NE.map (BL.fromStrict . TE.encodeUtf8) patches
|
||||||
|
@ -373,3 +379,4 @@ applyGitPatches shr rp branch patches = do
|
||||||
ExitSuccess -> return ()
|
ExitSuccess -> return ()
|
||||||
where
|
where
|
||||||
out2text = TU.decodeLenient . BL.toStrict
|
out2text = TU.decodeLenient . BL.toStrict
|
||||||
|
-}
|
||||||
|
|
|
@ -15,36 +15,27 @@
|
||||||
-}
|
-}
|
||||||
|
|
||||||
module Vervis.Handler.Client
|
module Vervis.Handler.Client
|
||||||
( getPublishR
|
( getResendVerifyEmailR
|
||||||
, postSharerOutboxR
|
, getActorKey1R
|
||||||
, postPublishR
|
, getActorKey2R
|
||||||
|
|
||||||
|
, getHomeR
|
||||||
, getBrowseR
|
, getBrowseR
|
||||||
|
|
||||||
, postSharerFollowR
|
|
||||||
, postProjectFollowR
|
|
||||||
, postProjectTicketFollowR
|
|
||||||
, postRepoFollowR
|
|
||||||
|
|
||||||
, postSharerUnfollowR
|
|
||||||
, postProjectUnfollowR
|
|
||||||
, postProjectTicketUnfollowR
|
|
||||||
, postRepoUnfollowR
|
|
||||||
|
|
||||||
, getNotificationsR
|
, getNotificationsR
|
||||||
, postNotificationsR
|
, postNotificationsR
|
||||||
|
, getPublishR
|
||||||
, postProjectTicketsR
|
, postPublishR
|
||||||
, postProjectTicketCloseR
|
, getInboxDebugR
|
||||||
, postProjectTicketOpenR
|
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
import Control.Applicative
|
import Control.Applicative
|
||||||
|
import Control.Concurrent.STM.TVar
|
||||||
import Control.Exception hiding (Handler)
|
import Control.Exception hiding (Handler)
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
import Control.Monad.Trans.Except
|
import Control.Monad.Trans.Except
|
||||||
import Data.Bitraversable
|
import Data.Bitraversable
|
||||||
|
import Data.List
|
||||||
import Data.Maybe
|
import Data.Maybe
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
import Data.Time.Clock
|
import Data.Time.Clock
|
||||||
|
@ -53,22 +44,26 @@ import Database.Persist
|
||||||
import Text.Blaze.Html (preEscapedToHtml)
|
import Text.Blaze.Html (preEscapedToHtml)
|
||||||
import Text.Blaze.Html.Renderer.Text
|
import Text.Blaze.Html.Renderer.Text
|
||||||
import Text.HTML.SanitizeXSS
|
import Text.HTML.SanitizeXSS
|
||||||
|
import Yesod.Auth
|
||||||
|
import Yesod.Auth.Account
|
||||||
|
import Yesod.Auth.Account.Message
|
||||||
import Yesod.Core
|
import Yesod.Core
|
||||||
import Yesod.Core.Widget
|
import Yesod.Core.Widget
|
||||||
import Yesod.Form
|
import Yesod.Form
|
||||||
import Yesod.Persist.Core
|
import Yesod.Persist.Core
|
||||||
|
|
||||||
|
import qualified Data.ByteString.Char8 as BC
|
||||||
import qualified Data.HashMap.Strict as M
|
import qualified Data.HashMap.Strict as M
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import qualified Data.Text.Encoding as TE
|
import qualified Data.Text.Encoding as TE
|
||||||
import qualified Data.Text.Lazy as TL
|
import qualified Data.Text.Lazy as TL
|
||||||
|
import qualified Data.Text.Lazy.Encoding as TLE
|
||||||
import qualified Database.Esqueleto as E
|
import qualified Database.Esqueleto as E
|
||||||
|
|
||||||
import Dvara
|
import Dvara
|
||||||
|
|
||||||
import Database.Persist.JSON
|
import Database.Persist.JSON
|
||||||
import Network.FedURI
|
import Network.FedURI
|
||||||
import Web.ActivityPub hiding (Ticket)
|
|
||||||
import Yesod.ActivityPub
|
import Yesod.ActivityPub
|
||||||
import Yesod.Auth.Unverified
|
import Yesod.Auth.Unverified
|
||||||
import Yesod.FedURI
|
import Yesod.FedURI
|
||||||
|
@ -84,10 +79,9 @@ import Database.Persist.Local
|
||||||
import Yesod.Persist.Local
|
import Yesod.Persist.Local
|
||||||
|
|
||||||
import Vervis.ActivityPub
|
import Vervis.ActivityPub
|
||||||
|
import Vervis.ActorKey
|
||||||
import Vervis.API
|
import Vervis.API
|
||||||
import Vervis.Client
|
|
||||||
import Vervis.FedURI
|
import Vervis.FedURI
|
||||||
import Vervis.Form.Ticket
|
|
||||||
import Vervis.Foundation
|
import Vervis.Foundation
|
||||||
import Vervis.Model
|
import Vervis.Model
|
||||||
import Vervis.Model.Ident
|
import Vervis.Model.Ident
|
||||||
|
@ -96,10 +90,111 @@ import Vervis.Path
|
||||||
import Vervis.Settings
|
import Vervis.Settings
|
||||||
import Vervis.Ticket
|
import Vervis.Ticket
|
||||||
|
|
||||||
import qualified Vervis.Client as C
|
|
||||||
import qualified Vervis.Darcs as D
|
import qualified Vervis.Darcs as D
|
||||||
import qualified Vervis.Git as G
|
import qualified Vervis.Git as G
|
||||||
|
|
||||||
|
-- | Account verification email resend form
|
||||||
|
getResendVerifyEmailR :: Handler Html
|
||||||
|
getResendVerifyEmailR = do
|
||||||
|
person <- requireUnverifiedAuth
|
||||||
|
defaultLayout $ do
|
||||||
|
setTitleI MsgEmailUnverified
|
||||||
|
[whamlet|
|
||||||
|
<p>_{MsgEmailUnverified}
|
||||||
|
^{resendVerifyEmailWidget (username person) AuthR}
|
||||||
|
|]
|
||||||
|
|
||||||
|
getActorKey
|
||||||
|
:: ((ActorKey, ActorKey, Bool) -> ActorKey)
|
||||||
|
-> Route App
|
||||||
|
-> Handler TypedContent
|
||||||
|
getActorKey choose route = do
|
||||||
|
actorKey <-
|
||||||
|
liftIO . fmap (actorKeyPublicBin . choose) . readTVarIO =<<
|
||||||
|
getsYesod appActorKeys
|
||||||
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
|
let key = AP.PublicKey
|
||||||
|
{ AP.publicKeyId = LocalRefURI $ Left $ encodeRouteLocal route
|
||||||
|
, AP.publicKeyExpires = Nothing
|
||||||
|
, AP.publicKeyOwner = AP.OwnerInstance
|
||||||
|
, AP.publicKeyMaterial = actorKey
|
||||||
|
}
|
||||||
|
provideHtmlAndAP key $ redirectToPrettyJSON route
|
||||||
|
|
||||||
|
getActorKey1R :: Handler TypedContent
|
||||||
|
getActorKey1R = getActorKey (\ (k1, _, _) -> k1) ActorKey1R
|
||||||
|
|
||||||
|
getActorKey2R :: Handler TypedContent
|
||||||
|
getActorKey2R = getActorKey (\ (_, k2, _) -> k2) ActorKey2R
|
||||||
|
|
||||||
|
getHomeR :: Handler Html
|
||||||
|
getHomeR = do
|
||||||
|
mp <- maybeAuth
|
||||||
|
case mp of
|
||||||
|
Just p -> personalOverview p
|
||||||
|
Nothing -> redirect BrowseR
|
||||||
|
where
|
||||||
|
personalOverview :: Entity Person -> Handler Html
|
||||||
|
personalOverview (Entity _pid _person) =
|
||||||
|
defaultLayout $ do
|
||||||
|
setTitle "Vervis > Overview"
|
||||||
|
$(widgetFile "personal-overview")
|
||||||
|
|
||||||
|
getBrowseR :: Handler Html
|
||||||
|
getBrowseR = do
|
||||||
|
(people, groups, repos, decks, looms) <- runDB $
|
||||||
|
(,,,,)
|
||||||
|
<$> (E.select $ E.from $ \ (person `E.InnerJoin` actor) -> do
|
||||||
|
E.on $ person E.^. PersonActor E.==. actor E.^. ActorId
|
||||||
|
E.orderBy [E.asc $ person E.^. PersonId]
|
||||||
|
return (person, actor)
|
||||||
|
)
|
||||||
|
<*> (E.select $ E.from $ \ (group `E.InnerJoin` actor) -> do
|
||||||
|
E.on $ group E.^. GroupActor E.==. actor E.^. ActorId
|
||||||
|
E.orderBy [E.asc $ group E.^. GroupId]
|
||||||
|
return (group, actor)
|
||||||
|
)
|
||||||
|
<*> (E.select $ E.from $ \ (repo `E.InnerJoin` actor) -> do
|
||||||
|
E.on $ repo E.^. RepoActor E.==. actor E.^. ActorId
|
||||||
|
E.orderBy [E.asc $ repo E.^. RepoId]
|
||||||
|
return (repo, actor)
|
||||||
|
)
|
||||||
|
<*> (E.select $ E.from $ \ (deck `E.InnerJoin` actor) -> do
|
||||||
|
E.on $ deck E.^. DeckActor E.==. actor E.^. ActorId
|
||||||
|
E.orderBy [E.asc $ deck E.^. DeckId]
|
||||||
|
return (deck, actor)
|
||||||
|
)
|
||||||
|
<*> (E.select $ E.from $ \ (loom `E.InnerJoin` actor) -> do
|
||||||
|
E.on $ loom E.^. LoomActor E.==. actor E.^. ActorId
|
||||||
|
E.orderBy [E.asc $ loom E.^. LoomId]
|
||||||
|
return (loom, actor)
|
||||||
|
)
|
||||||
|
{-
|
||||||
|
now <- liftIO getCurrentTime
|
||||||
|
repoRows <- forM repos $
|
||||||
|
\ (E.Value sharer, E.Value mproj, E.Value repo, E.Value vcs) -> do
|
||||||
|
path <- askRepoDir sharer repo
|
||||||
|
mlast <- case vcs of
|
||||||
|
VCSDarcs -> liftIO $ D.lastChange path now
|
||||||
|
VCSGit -> do
|
||||||
|
mt <- liftIO $ G.lastCommitTime path
|
||||||
|
return $ Just $ case mt of
|
||||||
|
Nothing -> Never
|
||||||
|
Just t ->
|
||||||
|
intervalToEventTime $
|
||||||
|
FriendlyConvert $
|
||||||
|
now `diffUTCTime` t
|
||||||
|
return (sharer, mproj, repo, vcs, mlast)
|
||||||
|
-}
|
||||||
|
hashPerson <- getEncodeKeyHashid
|
||||||
|
hashGroup <- getEncodeKeyHashid
|
||||||
|
hashRepo <- getEncodeKeyHashid
|
||||||
|
hashDeck <- getEncodeKeyHashid
|
||||||
|
hashLoom <- getEncodeKeyHashid
|
||||||
|
defaultLayout $ do
|
||||||
|
setTitle "Welcome to Vervis!"
|
||||||
|
$(widgetFile "browse")
|
||||||
|
|
||||||
getShowTime = showTime <$> liftIO getCurrentTime
|
getShowTime = showTime <$> liftIO getCurrentTime
|
||||||
where
|
where
|
||||||
showTime now =
|
showTime now =
|
||||||
|
@ -108,6 +203,16 @@ getShowTime = showTime <$> liftIO getCurrentTime
|
||||||
FriendlyConvert .
|
FriendlyConvert .
|
||||||
diffUTCTime now
|
diffUTCTime now
|
||||||
|
|
||||||
|
notificationForm :: Maybe (Maybe (InboxItemId, Bool)) -> Form (Maybe (InboxItemId, Bool))
|
||||||
|
notificationForm defs = renderDivs $ mk
|
||||||
|
<$> aopt hiddenField (name "Inbox Item ID#") (fmap fst <$> defs)
|
||||||
|
<*> aopt hiddenField (name "New unread flag") (fmap snd <$> defs)
|
||||||
|
where
|
||||||
|
name t = FieldSettings "" Nothing Nothing (Just t) []
|
||||||
|
mk Nothing Nothing = Nothing
|
||||||
|
mk (Just ibid) (Just unread) = Just (ibid, unread)
|
||||||
|
mk _ _ = error "Missing hidden field?"
|
||||||
|
|
||||||
objectSummary o =
|
objectSummary o =
|
||||||
case M.lookup "summary" o of
|
case M.lookup "summary" o of
|
||||||
Just (String t) | not (T.null t) -> Just t
|
Just (String t) | not (T.null t) -> Just t
|
||||||
|
@ -118,6 +223,166 @@ objectId o =
|
||||||
Just (String t) | not (T.null t) -> t
|
Just (String t) | not (T.null t) -> t
|
||||||
_ -> error "'id' field not found"
|
_ -> error "'id' field not found"
|
||||||
|
|
||||||
|
getNotificationsR :: Handler Html
|
||||||
|
getNotificationsR = do
|
||||||
|
Entity _ viewer <- requireVerifiedAuth
|
||||||
|
|
||||||
|
items <- runDB $ do
|
||||||
|
inboxID <- actorInbox <$> getJust (personActor viewer)
|
||||||
|
map adaptItem <$> getItems inboxID
|
||||||
|
|
||||||
|
notifications <- for items $ \ (ibiid, activity) -> do
|
||||||
|
((_result, widget), enctype) <-
|
||||||
|
runFormPost $ notificationForm $ Just $ Just (ibiid, False)
|
||||||
|
return (activity, widget, enctype)
|
||||||
|
|
||||||
|
((_result, widgetAll), enctypeAll) <-
|
||||||
|
runFormPost $ notificationForm $ Just Nothing
|
||||||
|
|
||||||
|
showTime <- getShowTime
|
||||||
|
defaultLayout $(widgetFile "person/notifications")
|
||||||
|
where
|
||||||
|
getItems ibid =
|
||||||
|
E.select $ E.from $
|
||||||
|
\ (ib `E.LeftOuterJoin` (ibl `E.InnerJoin` ob) `E.LeftOuterJoin` (ibr `E.InnerJoin` ract)) -> do
|
||||||
|
E.on $ ibr E.?. InboxItemRemoteActivity E.==. ract E.?. RemoteActivityId
|
||||||
|
E.on $ E.just (ib E.^. InboxItemId) E.==. ibr E.?. InboxItemRemoteItem
|
||||||
|
E.on $ ibl E.?. InboxItemLocalActivity E.==. ob E.?. OutboxItemId
|
||||||
|
E.on $ E.just (ib E.^. InboxItemId) E.==. ibl E.?. InboxItemLocalItem
|
||||||
|
E.where_
|
||||||
|
$ ( E.isNothing (ibr E.?. InboxItemRemoteInbox) E.||.
|
||||||
|
ibr E.?. InboxItemRemoteInbox E.==. E.just (E.val ibid)
|
||||||
|
)
|
||||||
|
E.&&.
|
||||||
|
( E.isNothing (ibl E.?. InboxItemLocalInbox) E.||.
|
||||||
|
ibl E.?. InboxItemLocalInbox E.==. E.just (E.val ibid)
|
||||||
|
)
|
||||||
|
E.&&.
|
||||||
|
ib E.^. InboxItemUnread E.==. E.val True
|
||||||
|
E.orderBy [E.desc $ ib E.^. InboxItemId]
|
||||||
|
return
|
||||||
|
( ib E.^. InboxItemId
|
||||||
|
, ob E.?. OutboxItemActivity
|
||||||
|
, ob E.?. OutboxItemPublished
|
||||||
|
, ract E.?. RemoteActivityContent
|
||||||
|
, ract E.?. RemoteActivityReceived
|
||||||
|
)
|
||||||
|
adaptItem
|
||||||
|
(E.Value ibid, E.Value mact, E.Value mpub, E.Value mobj, E.Value mrec) =
|
||||||
|
case (mact, mpub, mobj, mrec) of
|
||||||
|
(Nothing, Nothing, Nothing, Nothing) ->
|
||||||
|
error $ ibiidString ++ " neither local nor remote"
|
||||||
|
(Just _, Just _, Just _, Just _) ->
|
||||||
|
error $ ibiidString ++ " both local and remote"
|
||||||
|
(Just act, Just pub, Nothing, Nothing) ->
|
||||||
|
(ibid, (persistJSONObject act, (pub, False)))
|
||||||
|
(Nothing, Nothing, Just obj, Just rec) ->
|
||||||
|
(ibid, (persistJSONObject obj, (rec, True)))
|
||||||
|
_ -> error $ "Unexpected query result for " ++ ibiidString
|
||||||
|
where
|
||||||
|
ibiidString = "InboxItem #" ++ show (E.fromSqlKey ibid)
|
||||||
|
|
||||||
|
postNotificationsR :: Handler Html
|
||||||
|
postNotificationsR = do
|
||||||
|
Entity _ poster <- requireVerifiedAuth
|
||||||
|
|
||||||
|
((result, _widget), _enctype) <- runFormPost $ notificationForm Nothing
|
||||||
|
|
||||||
|
case result of
|
||||||
|
FormMissing -> setMessage "Field(s) missing"
|
||||||
|
FormFailure l ->
|
||||||
|
setMessage $ toHtml $ "Marking as read failed:" <> T.pack (show l)
|
||||||
|
FormSuccess mitem -> do
|
||||||
|
(multi, markedUnread) <- runDB $ do
|
||||||
|
inboxID <- actorInbox <$> getJust (personActor poster)
|
||||||
|
case mitem of
|
||||||
|
Nothing -> do
|
||||||
|
ibiids <- map E.unValue <$> getItems inboxID
|
||||||
|
updateWhere
|
||||||
|
[InboxItemId <-. ibiids]
|
||||||
|
[InboxItemUnread =. False]
|
||||||
|
return (True, False)
|
||||||
|
Just (ibiid, unread) -> do
|
||||||
|
mib <-
|
||||||
|
requireEitherAlt
|
||||||
|
(getValBy $ UniqueInboxItemLocalItem ibiid)
|
||||||
|
(getValBy $ UniqueInboxItemRemoteItem ibiid)
|
||||||
|
"Unused InboxItem"
|
||||||
|
"InboxItem used more than once"
|
||||||
|
let samePid =
|
||||||
|
case mib of
|
||||||
|
Left ibl ->
|
||||||
|
inboxItemLocalInbox ibl == inboxID
|
||||||
|
Right ibr ->
|
||||||
|
inboxItemRemoteInbox ibr == inboxID
|
||||||
|
if samePid
|
||||||
|
then do
|
||||||
|
update ibiid [InboxItemUnread =. unread]
|
||||||
|
return (False, unread)
|
||||||
|
else
|
||||||
|
permissionDenied
|
||||||
|
"Notification belongs to different user"
|
||||||
|
setMessage $
|
||||||
|
if multi
|
||||||
|
then "Items marked as read."
|
||||||
|
else if markedUnread
|
||||||
|
then "Item marked as unread."
|
||||||
|
else "Item marked as read."
|
||||||
|
|
||||||
|
redirect NotificationsR
|
||||||
|
where
|
||||||
|
getItems ibid =
|
||||||
|
E.select $ E.from $
|
||||||
|
\ (ib `E.LeftOuterJoin` ibl `E.LeftOuterJoin` ibr) -> do
|
||||||
|
E.on $ E.just (ib E.^. InboxItemId) E.==. ibr E.?. InboxItemRemoteItem
|
||||||
|
E.on $ E.just (ib E.^. InboxItemId) E.==. ibl E.?. InboxItemLocalItem
|
||||||
|
E.where_
|
||||||
|
$ ( E.isNothing (ibr E.?. InboxItemRemoteInbox) E.||.
|
||||||
|
ibr E.?. InboxItemRemoteInbox E.==. E.just (E.val ibid)
|
||||||
|
)
|
||||||
|
E.&&.
|
||||||
|
( E.isNothing (ibl E.?. InboxItemLocalInbox) E.||.
|
||||||
|
ibl E.?. InboxItemLocalInbox E.==. E.just (E.val ibid)
|
||||||
|
)
|
||||||
|
E.&&.
|
||||||
|
ib E.^. InboxItemUnread E.==. E.val True
|
||||||
|
return $ ib E.^. InboxItemId
|
||||||
|
|
||||||
|
getPublishR :: Handler Html
|
||||||
|
getPublishR = do
|
||||||
|
error "Temporarily disabled"
|
||||||
|
|
||||||
|
postPublishR :: Handler Html
|
||||||
|
postPublishR = do
|
||||||
|
error "Temporarily disabled"
|
||||||
|
|
||||||
|
getInboxDebugR :: Handler Html
|
||||||
|
getInboxDebugR = do
|
||||||
|
acts <-
|
||||||
|
liftIO . readTVarIO . snd =<< maybe notFound return =<< getsYesod appActivities
|
||||||
|
defaultLayout
|
||||||
|
[whamlet|
|
||||||
|
<p>
|
||||||
|
Welcome to the ActivityPub inbox test page! Activities received
|
||||||
|
by this Vervis instance are listed here for testing and
|
||||||
|
debugging. To test, go to another Vervis instance and publish
|
||||||
|
something that supports federation, either through the regular UI
|
||||||
|
or via the /publish page, and then come back here to see the
|
||||||
|
result. Activities that aren't understood or their processing
|
||||||
|
fails get listed here too, with a report of what exactly
|
||||||
|
happened.
|
||||||
|
<p>Last 10 activities posted:
|
||||||
|
<ul>
|
||||||
|
$forall ActivityReport time msg ctypes body <- acts
|
||||||
|
<li>
|
||||||
|
<div>#{show time}
|
||||||
|
<div>#{msg}
|
||||||
|
<div><code>#{intercalate " | " $ map BC.unpack ctypes}
|
||||||
|
<div><pre>#{TLE.decodeUtf8 body}
|
||||||
|
|]
|
||||||
|
|
||||||
|
{-
|
||||||
|
|
||||||
fedUriField
|
fedUriField
|
||||||
:: (Monad m, RenderMessage (HandlerSite m) FormMessage) => Field m FedURI
|
:: (Monad m, RenderMessage (HandlerSite m) FormMessage) => Field m FedURI
|
||||||
fedUriField = Field
|
fedUriField = Field
|
||||||
|
@ -348,63 +613,6 @@ getPublishR = do
|
||||||
widget7 enctype7
|
widget7 enctype7
|
||||||
widget8 enctype8
|
widget8 enctype8
|
||||||
|
|
||||||
postSharerOutboxR :: ShrIdent -> Handler Text
|
|
||||||
postSharerOutboxR shr = do
|
|
||||||
federation <- getsYesod $ appFederation . appSettings
|
|
||||||
unless federation badMethod
|
|
||||||
(ep@(Entity pid person), sharer) <- runDB $ do
|
|
||||||
Entity sid s <- getBy404 $ UniqueSharer shr
|
|
||||||
(,s) <$> getBy404 (UniquePersonIdent sid)
|
|
||||||
(_app, mpid, _scopes) <- maybe notAuthenticated return =<< getDvaraAuth
|
|
||||||
pid' <-
|
|
||||||
maybe (permissionDenied "Not authorized to post as a user") return mpid
|
|
||||||
unless (pid == pid') $
|
|
||||||
permissionDenied "Can't post as other users"
|
|
||||||
verifyContentTypeAP
|
|
||||||
Doc h activity <- requireInsecureJsonBody
|
|
||||||
hl <- hostIsLocal h
|
|
||||||
unless hl $ invalidArgs ["Activity host isn't the instance host"]
|
|
||||||
result <- runExceptT $ handle ep sharer activity
|
|
||||||
case result of
|
|
||||||
Left err -> invalidArgs [err]
|
|
||||||
Right obiid -> do
|
|
||||||
obikhid <- encodeKeyHashid obiid
|
|
||||||
sendResponseCreated $ SharerOutboxItemR shr obikhid
|
|
||||||
where
|
|
||||||
handle eperson sharer (Activity _mid actor mcap summary audience specific) = do
|
|
||||||
case decodeRouteLocal actor of
|
|
||||||
Just (SharerR shr') | shr' == shr -> return ()
|
|
||||||
_ -> throwE "Can't post activity sttributed to someone else"
|
|
||||||
case specific of
|
|
||||||
AddActivity (AP.Add obj target) ->
|
|
||||||
case obj of
|
|
||||||
Right (AddBundle patches) ->
|
|
||||||
addBundleC eperson sharer summary audience patches target
|
|
||||||
_ -> throwE "Unsupported Add 'object' type"
|
|
||||||
ApplyActivity apply ->
|
|
||||||
applyC eperson sharer summary audience mcap apply
|
|
||||||
CreateActivity (Create obj mtarget) ->
|
|
||||||
case obj of
|
|
||||||
CreateNote _ note ->
|
|
||||||
createNoteC eperson sharer summary audience note mtarget
|
|
||||||
CreateTicket _ ticket ->
|
|
||||||
createTicketC eperson sharer summary audience ticket mtarget
|
|
||||||
_ -> throwE "Unsupported Create 'object' type"
|
|
||||||
FollowActivity follow ->
|
|
||||||
followC shr summary audience follow
|
|
||||||
OfferActivity (Offer obj target) ->
|
|
||||||
case obj of
|
|
||||||
OfferTicket ticket ->
|
|
||||||
offerTicketC eperson sharer summary audience ticket target
|
|
||||||
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 eperson sharer summary audience undo
|
|
||||||
_ -> throwE "Unsupported activity type"
|
|
||||||
|
|
||||||
data Result
|
data Result
|
||||||
= ResultPublishComment ((Host, ShrIdent, PrjIdent, KeyHashid LocalTicket), Maybe FedURI, Text)
|
= ResultPublishComment ((Host, ShrIdent, PrjIdent, KeyHashid LocalTicket), Maybe FedURI, Text)
|
||||||
| ResultCreateTicket (FedURI, FedURI, TextHtml, TextPandocMarkdown)
|
| ResultCreateTicket (FedURI, FedURI, TextHtml, TextPandocMarkdown)
|
||||||
|
@ -587,54 +795,6 @@ postPublishR = do
|
||||||
C.follow shrAuthor uObject uRecip False
|
C.follow shrAuthor uObject uRecip False
|
||||||
followC shrAuthor (Just summary) audience followAP
|
followC shrAuthor (Just summary) audience followAP
|
||||||
|
|
||||||
getBrowseR :: Handler Html
|
|
||||||
getBrowseR = do
|
|
||||||
(rowsRepo, rowsProject) <- do
|
|
||||||
(repos, projects) <- runDB $ do
|
|
||||||
rs <- E.select $ E.from $
|
|
||||||
\ (repo `E.LeftOuterJoin` project `E.InnerJoin` sharer) -> do
|
|
||||||
E.on $ repo E.^. RepoSharer E.==. sharer E.^. SharerId
|
|
||||||
E.on $ repo E.^. RepoProject E.==. project E.?. ProjectId
|
|
||||||
E.orderBy
|
|
||||||
[ E.asc $ sharer E.^. SharerIdent
|
|
||||||
, E.asc $ project E.?. ProjectIdent
|
|
||||||
, E.asc $ repo E.^. RepoIdent
|
|
||||||
]
|
|
||||||
return
|
|
||||||
( sharer E.^. SharerIdent
|
|
||||||
, project E.?. ProjectIdent
|
|
||||||
, repo E.^. RepoIdent
|
|
||||||
, repo E.^. RepoVcs
|
|
||||||
)
|
|
||||||
js <- E.select $ E.from $ \ (j `E.InnerJoin` s `E.LeftOuterJoin` r) -> do
|
|
||||||
E.on $ E.just (j E.^. ProjectId) E.==. E.joinV (r E.?. RepoProject)
|
|
||||||
E.on $ j E.^. ProjectSharer E.==. s E.^. SharerId
|
|
||||||
E.where_ $ E.isNothing $ r E.?. RepoId
|
|
||||||
return
|
|
||||||
( s E.^. SharerIdent
|
|
||||||
, j E.^. ProjectIdent
|
|
||||||
)
|
|
||||||
return (rs, js)
|
|
||||||
now <- liftIO getCurrentTime
|
|
||||||
repoRows <- forM repos $
|
|
||||||
\ (E.Value sharer, E.Value mproj, E.Value repo, E.Value vcs) -> do
|
|
||||||
path <- askRepoDir sharer repo
|
|
||||||
mlast <- case vcs of
|
|
||||||
VCSDarcs -> liftIO $ D.lastChange path now
|
|
||||||
VCSGit -> do
|
|
||||||
mt <- liftIO $ G.lastCommitTime path
|
|
||||||
return $ Just $ case mt of
|
|
||||||
Nothing -> Never
|
|
||||||
Just t ->
|
|
||||||
intervalToEventTime $
|
|
||||||
FriendlyConvert $
|
|
||||||
now `diffUTCTime` t
|
|
||||||
return (sharer, mproj, repo, vcs, mlast)
|
|
||||||
return (repoRows, projects)
|
|
||||||
defaultLayout $ do
|
|
||||||
setTitle "Welcome to Vervis!"
|
|
||||||
$(widgetFile "homepage")
|
|
||||||
|
|
||||||
setFollowMessage :: ShrIdent -> Either Text OutboxItemId -> Handler ()
|
setFollowMessage :: ShrIdent -> Either Text OutboxItemId -> Handler ()
|
||||||
setFollowMessage _ (Left err) = setMessage $ toHtml err
|
setFollowMessage _ (Left err) = setMessage $ toHtml err
|
||||||
setFollowMessage shr (Right obiid) = do
|
setFollowMessage shr (Right obiid) = do
|
||||||
|
@ -733,146 +893,6 @@ postRepoUnfollowR shrFollowee rpFollowee = do
|
||||||
setUnfollowMessage shrAuthor eid
|
setUnfollowMessage shrAuthor eid
|
||||||
redirect $ RepoR shrFollowee rpFollowee
|
redirect $ RepoR shrFollowee rpFollowee
|
||||||
|
|
||||||
notificationForm :: Maybe (Maybe (InboxItemId, Bool)) -> Form (Maybe (InboxItemId, Bool))
|
|
||||||
notificationForm defs = renderDivs $ mk
|
|
||||||
<$> aopt hiddenField (name "Inbox Item ID#") (fmap fst <$> defs)
|
|
||||||
<*> aopt hiddenField (name "New unread flag") (fmap snd <$> defs)
|
|
||||||
where
|
|
||||||
name t = FieldSettings "" Nothing Nothing (Just t) []
|
|
||||||
mk Nothing Nothing = Nothing
|
|
||||||
mk (Just ibid) (Just unread) = Just (ibid, unread)
|
|
||||||
mk _ _ = error "Missing hidden field?"
|
|
||||||
|
|
||||||
getNotificationsR :: ShrIdent -> Handler Html
|
|
||||||
getNotificationsR shr = do
|
|
||||||
items <- runDB $ do
|
|
||||||
sid <- getKeyBy404 $ UniqueSharer shr
|
|
||||||
p <- getValBy404 $ UniquePersonIdent sid
|
|
||||||
let ibid = personInbox p
|
|
||||||
map adaptItem <$> getItems ibid
|
|
||||||
notifications <- for items $ \ (ibiid, activity) -> do
|
|
||||||
((_result, widget), enctype) <-
|
|
||||||
runFormPost $ notificationForm $ Just $ Just (ibiid, False)
|
|
||||||
return (activity, widget, enctype)
|
|
||||||
((_result, widgetAll), enctypeAll) <-
|
|
||||||
runFormPost $ notificationForm $ Just Nothing
|
|
||||||
showTime <- getShowTime
|
|
||||||
defaultLayout $(widgetFile "person/notifications")
|
|
||||||
where
|
|
||||||
getItems ibid =
|
|
||||||
E.select $ E.from $
|
|
||||||
\ (ib `E.LeftOuterJoin` (ibl `E.InnerJoin` ob) `E.LeftOuterJoin` (ibr `E.InnerJoin` ract)) -> do
|
|
||||||
E.on $ ibr E.?. InboxItemRemoteActivity E.==. ract E.?. RemoteActivityId
|
|
||||||
E.on $ E.just (ib E.^. InboxItemId) E.==. ibr E.?. InboxItemRemoteItem
|
|
||||||
E.on $ ibl E.?. InboxItemLocalActivity E.==. ob E.?. OutboxItemId
|
|
||||||
E.on $ E.just (ib E.^. InboxItemId) E.==. ibl E.?. InboxItemLocalItem
|
|
||||||
E.where_
|
|
||||||
$ ( E.isNothing (ibr E.?. InboxItemRemoteInbox) E.||.
|
|
||||||
ibr E.?. InboxItemRemoteInbox E.==. E.just (E.val ibid)
|
|
||||||
)
|
|
||||||
E.&&.
|
|
||||||
( E.isNothing (ibl E.?. InboxItemLocalInbox) E.||.
|
|
||||||
ibl E.?. InboxItemLocalInbox E.==. E.just (E.val ibid)
|
|
||||||
)
|
|
||||||
E.&&.
|
|
||||||
ib E.^. InboxItemUnread E.==. E.val True
|
|
||||||
E.orderBy [E.desc $ ib E.^. InboxItemId]
|
|
||||||
return
|
|
||||||
( ib E.^. InboxItemId
|
|
||||||
, ob E.?. OutboxItemActivity
|
|
||||||
, ob E.?. OutboxItemPublished
|
|
||||||
, ract E.?. RemoteActivityContent
|
|
||||||
, ract E.?. RemoteActivityReceived
|
|
||||||
)
|
|
||||||
adaptItem
|
|
||||||
(E.Value ibid, E.Value mact, E.Value mpub, E.Value mobj, E.Value mrec) =
|
|
||||||
case (mact, mpub, mobj, mrec) of
|
|
||||||
(Nothing, Nothing, Nothing, Nothing) ->
|
|
||||||
error $ ibiidString ++ " neither local nor remote"
|
|
||||||
(Just _, Just _, Just _, Just _) ->
|
|
||||||
error $ ibiidString ++ " both local and remote"
|
|
||||||
(Just act, Just pub, Nothing, Nothing) ->
|
|
||||||
(ibid, (persistJSONObject act, (pub, False)))
|
|
||||||
(Nothing, Nothing, Just obj, Just rec) ->
|
|
||||||
(ibid, (persistJSONObject obj, (rec, True)))
|
|
||||||
_ -> error $ "Unexpected query result for " ++ ibiidString
|
|
||||||
where
|
|
||||||
ibiidString = "InboxItem #" ++ show (E.fromSqlKey ibid)
|
|
||||||
|
|
||||||
postNotificationsR :: ShrIdent -> Handler Html
|
|
||||||
postNotificationsR shr = do
|
|
||||||
((result, _widget), _enctype) <- runFormPost $ notificationForm Nothing
|
|
||||||
case result of
|
|
||||||
FormSuccess mitem -> do
|
|
||||||
(multi, markedUnread) <- runDB $ do
|
|
||||||
sid <- getKeyBy404 $ UniqueSharer shr
|
|
||||||
p <- getValBy404 $ UniquePersonIdent sid
|
|
||||||
let ibid = personInbox p
|
|
||||||
case mitem of
|
|
||||||
Nothing -> do
|
|
||||||
ibiids <- map E.unValue <$> getItems ibid
|
|
||||||
updateWhere
|
|
||||||
[InboxItemId <-. ibiids]
|
|
||||||
[InboxItemUnread =. False]
|
|
||||||
return (True, False)
|
|
||||||
Just (ibiid, unread) -> do
|
|
||||||
mibl <- getValBy $ UniqueInboxItemLocalItem ibiid
|
|
||||||
mibr <- getValBy $ UniqueInboxItemRemoteItem ibiid
|
|
||||||
mib <-
|
|
||||||
requireEitherM
|
|
||||||
mibl
|
|
||||||
mibr
|
|
||||||
"Unused InboxItem"
|
|
||||||
"InboxItem used more than once"
|
|
||||||
let samePid =
|
|
||||||
case mib of
|
|
||||||
Left ibl ->
|
|
||||||
inboxItemLocalInbox ibl == ibid
|
|
||||||
Right ibr ->
|
|
||||||
inboxItemRemoteInbox ibr == ibid
|
|
||||||
if samePid
|
|
||||||
then do
|
|
||||||
update ibiid [InboxItemUnread =. unread]
|
|
||||||
return (False, unread)
|
|
||||||
else
|
|
||||||
permissionDenied
|
|
||||||
"Notification belongs to different user"
|
|
||||||
setMessage $
|
|
||||||
if multi
|
|
||||||
then "Items marked as read."
|
|
||||||
else if markedUnread
|
|
||||||
then "Item marked as unread."
|
|
||||||
else "Item marked as read."
|
|
||||||
FormMissing -> do
|
|
||||||
setMessage "Field(s) missing"
|
|
||||||
FormFailure l -> do
|
|
||||||
setMessage $ toHtml $ "Marking as read failed:" <> T.pack (show l)
|
|
||||||
redirect $ NotificationsR shr
|
|
||||||
where
|
|
||||||
getItems ibid =
|
|
||||||
E.select $ E.from $
|
|
||||||
\ (ib `E.LeftOuterJoin` ibl `E.LeftOuterJoin` ibr) -> do
|
|
||||||
E.on $ E.just (ib E.^. InboxItemId) E.==. ibr E.?. InboxItemRemoteItem
|
|
||||||
E.on $ E.just (ib E.^. InboxItemId) E.==. ibl E.?. InboxItemLocalItem
|
|
||||||
E.where_
|
|
||||||
$ ( E.isNothing (ibr E.?. InboxItemRemoteInbox) E.||.
|
|
||||||
ibr E.?. InboxItemRemoteInbox E.==. E.just (E.val ibid)
|
|
||||||
)
|
|
||||||
E.&&.
|
|
||||||
( E.isNothing (ibl E.?. InboxItemLocalInbox) E.||.
|
|
||||||
ibl E.?. InboxItemLocalInbox E.==. E.just (E.val ibid)
|
|
||||||
)
|
|
||||||
E.&&.
|
|
||||||
ib E.^. InboxItemUnread E.==. E.val True
|
|
||||||
return $ ib E.^. InboxItemId
|
|
||||||
-- TODO copied from Vervis.Federation, put this in 1 place
|
|
||||||
requireEitherM
|
|
||||||
:: MonadIO m => Maybe a -> Maybe b -> String -> String -> m (Either a b)
|
|
||||||
requireEitherM mx my f t =
|
|
||||||
case requireEither mx my of
|
|
||||||
Left b -> liftIO $ throwIO $ userError $ if b then t else f
|
|
||||||
Right exy -> return exy
|
|
||||||
|
|
||||||
postProjectTicketsR :: ShrIdent -> PrjIdent -> Handler Html
|
postProjectTicketsR :: ShrIdent -> PrjIdent -> Handler Html
|
||||||
postProjectTicketsR shr prj = do
|
postProjectTicketsR shr prj = do
|
||||||
wid <- runDB $ do
|
wid <- runDB $ do
|
||||||
|
@ -989,3 +1009,4 @@ postProjectTicketOpenR shr prj ltkhid = do
|
||||||
Left e -> setMessage $ toHtml $ "Error: " <> e
|
Left e -> setMessage $ toHtml $ "Error: " <> e
|
||||||
Right _obiid -> setMessage "Ticket reopened"
|
Right _obiid -> setMessage "Ticket reopened"
|
||||||
redirect $ ProjectTicketR shr prj ltkhid
|
redirect $ ProjectTicketR shr prj ltkhid
|
||||||
|
-}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{- This file is part of Vervis.
|
{- This file is part of Vervis.
|
||||||
-
|
-
|
||||||
- Written in 2020 by fr33domlover <fr33domlover@riseup.net>.
|
- Written in 2020, 2022 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,8 +13,28 @@
|
||||||
- <http://creativecommons.org/publicdomain/zero/1.0/>.
|
- <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
-}
|
-}
|
||||||
|
|
||||||
module Vervis.Handler.Patch
|
module Vervis.Handler.Cloth
|
||||||
( getSharerProposalsR
|
( getClothR
|
||||||
|
, getClothDiscussionR
|
||||||
|
, getClothEventsR
|
||||||
|
, getClothFollowersR
|
||||||
|
, getClothDepsR
|
||||||
|
, getClothReverseDepsR
|
||||||
|
|
||||||
|
, getBundleR
|
||||||
|
, getPatchR
|
||||||
|
|
||||||
|
, getClothDepR
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{-
|
||||||
|
, getSharerProposalsR
|
||||||
, getSharerProposalR
|
, getSharerProposalR
|
||||||
, getSharerProposalDiscussionR
|
, getSharerProposalDiscussionR
|
||||||
, getSharerProposalDepsR
|
, getSharerProposalDepsR
|
||||||
|
@ -33,6 +53,7 @@ module Vervis.Handler.Patch
|
||||||
, getRepoProposalEventsR
|
, getRepoProposalEventsR
|
||||||
, getRepoProposalBundleR
|
, getRepoProposalBundleR
|
||||||
, getRepoProposalBundlePatchR
|
, getRepoProposalBundlePatchR
|
||||||
|
-}
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
|
@ -51,6 +72,7 @@ import qualified Data.List.NonEmpty as NE
|
||||||
import qualified Data.List.Ordered as LO
|
import qualified Data.List.Ordered as LO
|
||||||
import qualified Database.Esqueleto as E
|
import qualified Database.Esqueleto as E
|
||||||
|
|
||||||
|
import Development.PatchMediaType
|
||||||
import Network.FedURI
|
import Network.FedURI
|
||||||
import Web.ActivityPub hiding (Ticket (..), Patch (..), Bundle (..), Repo (..))
|
import Web.ActivityPub hiding (Ticket (..), Patch (..), Bundle (..), Repo (..))
|
||||||
import Yesod.ActivityPub
|
import Yesod.ActivityPub
|
||||||
|
@ -60,21 +82,432 @@ import Yesod.Hashids
|
||||||
import qualified Web.ActivityPub as AP
|
import qualified Web.ActivityPub as AP
|
||||||
|
|
||||||
import Data.Paginate.Local
|
import Data.Paginate.Local
|
||||||
|
import Database.Persist.Local
|
||||||
import Yesod.Persist.Local
|
import Yesod.Persist.Local
|
||||||
|
|
||||||
import Vervis.ActivityPub
|
import Vervis.ActivityPub
|
||||||
|
import Vervis.Actor
|
||||||
import Vervis.API
|
import Vervis.API
|
||||||
|
import Vervis.Cloth
|
||||||
import Vervis.Discussion
|
import Vervis.Discussion
|
||||||
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 Development.PatchMediaType
|
|
||||||
import Vervis.Model.Ticket
|
import Vervis.Model.Ticket
|
||||||
import Vervis.Paginate
|
import Vervis.Paginate
|
||||||
import Vervis.Patch
|
|
||||||
import Vervis.Ticket
|
import Vervis.Ticket
|
||||||
|
|
||||||
|
getClothR :: KeyHashid Loom -> KeyHashid TicketLoom -> Handler TypedContent
|
||||||
|
getClothR loomHash clothHash = do
|
||||||
|
(repoID, mbranch, ticket, author, resolve, bundleID) <- runDB $ do
|
||||||
|
(Entity _ loom, Entity _ cloth, Entity _ ticket', author', resolve', bundleID' :| _) <-
|
||||||
|
getCloth404 loomHash clothHash
|
||||||
|
(,,,,,)
|
||||||
|
(loomRepo loom)
|
||||||
|
(ticketLoomBranch cloth)
|
||||||
|
ticket'
|
||||||
|
<$> (case author' of
|
||||||
|
Left (Entity _ tal) ->
|
||||||
|
return $ Left $ ticketAuthorLocalAuthor tal
|
||||||
|
Right (Entity _ tar) -> Right <$> do
|
||||||
|
ra <- getJust $ ticketAuthorRemoteAuthor tar
|
||||||
|
ro <- getJust $ remoteActorIdent ra
|
||||||
|
i <- getJust $ remoteObjectInstance ro
|
||||||
|
return (i, ro)
|
||||||
|
)
|
||||||
|
<*> (for resolve' $ \ (_, etrx) ->
|
||||||
|
bitraverse
|
||||||
|
(\ (Entity _ trl) -> do
|
||||||
|
let obiid = ticketResolveLocalActivity trl
|
||||||
|
obid <- outboxItemOutbox <$> getJust obiid
|
||||||
|
actorID <- do
|
||||||
|
maybeActorID <- getKeyBy $ UniqueActorOutbox obid
|
||||||
|
case maybeActorID of
|
||||||
|
Nothing -> error "Found outbox not used by any actor"
|
||||||
|
Just a -> return a
|
||||||
|
actor <- getLocalActor actorID
|
||||||
|
return (actor, obiid)
|
||||||
|
)
|
||||||
|
(\ (Entity _ trr) -> do
|
||||||
|
roid <-
|
||||||
|
remoteActivityIdent <$>
|
||||||
|
getJust (ticketResolveRemoteActivity trr)
|
||||||
|
ro <- getJust roid
|
||||||
|
i <- getJust $ remoteObjectInstance ro
|
||||||
|
return (i, ro)
|
||||||
|
)
|
||||||
|
etrx
|
||||||
|
)
|
||||||
|
<*> pure bundleID'
|
||||||
|
|
||||||
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
|
encodeRouteHome <- getEncodeRouteHome
|
||||||
|
hashPerson <- getEncodeKeyHashid
|
||||||
|
hashItem <- getEncodeKeyHashid
|
||||||
|
hLocal <- getsYesod siteInstanceHost
|
||||||
|
repoHash <- encodeKeyHashid repoID
|
||||||
|
bundleHash <- encodeKeyHashid bundleID
|
||||||
|
let route mk = encodeRouteLocal $ mk loomHash clothHash
|
||||||
|
authorHost =
|
||||||
|
case author of
|
||||||
|
Left _ -> hLocal
|
||||||
|
Right (i, _) -> instanceHost i
|
||||||
|
ticketLocalAP = AP.TicketLocal
|
||||||
|
{ AP.ticketId = route ClothR
|
||||||
|
, AP.ticketReplies = route ClothDiscussionR
|
||||||
|
, AP.ticketParticipants = route ClothFollowersR
|
||||||
|
, AP.ticketTeam = Nothing
|
||||||
|
, AP.ticketEvents = route ClothEventsR
|
||||||
|
, AP.ticketDeps = route ClothDepsR
|
||||||
|
, AP.ticketReverseDeps = route ClothReverseDepsR
|
||||||
|
}
|
||||||
|
mergeRequestAP = AP.MergeRequest
|
||||||
|
{ AP.mrOrigin = Nothing
|
||||||
|
, AP.mrTarget =
|
||||||
|
case mbranch of
|
||||||
|
Nothing -> Left $ encodeRouteLocal $ RepoR repoHash
|
||||||
|
Just b -> Right AP.Branch
|
||||||
|
{ AP.branchName = b
|
||||||
|
, AP.branchRef = "refs/heads/" <> b
|
||||||
|
, AP.branchRepo = encodeRouteLocal $ RepoR repoHash
|
||||||
|
}
|
||||||
|
, AP.mrBundle =
|
||||||
|
Left $ encodeRouteHome $ BundleR loomHash clothHash bundleHash
|
||||||
|
}
|
||||||
|
ticketAP = AP.Ticket
|
||||||
|
{ AP.ticketLocal = Just (hLocal, ticketLocalAP)
|
||||||
|
, AP.ticketAttributedTo =
|
||||||
|
case author of
|
||||||
|
Left authorID ->
|
||||||
|
encodeRouteLocal $ PersonR $ hashPerson authorID
|
||||||
|
Right (_instance, object) ->
|
||||||
|
remoteObjectIdent object
|
||||||
|
, AP.ticketPublished = Just $ ticketCreated ticket
|
||||||
|
, AP.ticketUpdated = Nothing
|
||||||
|
, AP.ticketContext = Just $ encodeRouteHome $ LoomR loomHash
|
||||||
|
-- , AP.ticketName = Just $ "#" <> T.pack (show num)
|
||||||
|
, AP.ticketSummary = TextHtml $ ticketTitle ticket
|
||||||
|
, AP.ticketContent = TextHtml $ ticketDescription ticket
|
||||||
|
, AP.ticketSource = TextPandocMarkdown $ ticketSource ticket
|
||||||
|
, AP.ticketAssignedTo = Nothing
|
||||||
|
, AP.ticketResolved =
|
||||||
|
let u (Left (actor, obiid)) =
|
||||||
|
encodeRouteHome $
|
||||||
|
outboxItemRoute actor $ hashItem obiid
|
||||||
|
u (Right (i, ro)) =
|
||||||
|
ObjURI (instanceHost i) (remoteObjectIdent ro)
|
||||||
|
in (,Nothing) . Just . u <$> resolve
|
||||||
|
, AP.ticketAttachment = Just (hLocal, mergeRequestAP)
|
||||||
|
}
|
||||||
|
|
||||||
|
provideHtmlAndAP' authorHost ticketAP $ redirectToPrettyJSON here
|
||||||
|
where
|
||||||
|
here = ClothR loomHash clothHash
|
||||||
|
|
||||||
|
{-
|
||||||
|
mpid <- maybeAuthId
|
||||||
|
( wshr, wfl,
|
||||||
|
author, massignee, mresolved, cloth, lcloth, tparams, eparams, cparams) <-
|
||||||
|
runDB $ do
|
||||||
|
(Entity sid sharer, Entity jid project, Entity tid cloth, Entity _ lcloth, _etcl, _etpl, author, resolved) <- getProjectCloth404 shar proj ltkhid
|
||||||
|
tparams <- getClothTextParams tid wid
|
||||||
|
eparams <- getClothEnumParams tid wid
|
||||||
|
cparams <- getClothClasses tid wid
|
||||||
|
return
|
||||||
|
( wshr, wfl
|
||||||
|
, author', massignee, mresolved, cloth, lcloth
|
||||||
|
, tparams, eparams, cparams
|
||||||
|
)
|
||||||
|
let desc :: Widget
|
||||||
|
desc = toWidget $ preEscapedToMarkup $ clothDescription cloth
|
||||||
|
discuss =
|
||||||
|
discussionW
|
||||||
|
(return $ localClothDiscuss lcloth)
|
||||||
|
(ProjectClothTopReplyR shar proj ltkhid)
|
||||||
|
(ProjectClothReplyR shar proj ltkhid . encodeHid)
|
||||||
|
cRelevant <- newIdent
|
||||||
|
cIrrelevant <- newIdent
|
||||||
|
let relevant filt =
|
||||||
|
bool cIrrelevant cRelevant $
|
||||||
|
case clothStatus cloth of
|
||||||
|
TSNew -> wffNew filt
|
||||||
|
TSTodo -> wffTodo filt
|
||||||
|
TSClosed -> wffClosed filt
|
||||||
|
provideHtmlAndAP' host clothAP $
|
||||||
|
let followButton =
|
||||||
|
followW
|
||||||
|
(ProjectClothFollowR shar proj ltkhid)
|
||||||
|
(ProjectClothUnfollowR shar proj ltkhid)
|
||||||
|
(return $ localClothFollowers lcloth)
|
||||||
|
in $(widgetFile "cloth/one")
|
||||||
|
-}
|
||||||
|
|
||||||
|
getClothDiscussionR
|
||||||
|
:: KeyHashid Loom -> KeyHashid TicketLoom -> Handler TypedContent
|
||||||
|
getClothDiscussionR _ _ = do
|
||||||
|
error "Temporarily disabled"
|
||||||
|
{-
|
||||||
|
encodeHid <- getEncodeKeyHashid
|
||||||
|
getDiscussion
|
||||||
|
(ProjectClothReplyR shar proj ltkhid . encodeHid)
|
||||||
|
(ProjectClothTopReplyR shar proj ltkhid)
|
||||||
|
(selectDiscussionId shar proj ltkhid)
|
||||||
|
-}
|
||||||
|
|
||||||
|
getClothEventsR
|
||||||
|
:: KeyHashid Loom -> KeyHashid TicketLoom -> Handler TypedContent
|
||||||
|
getClothEventsR _ _ = do
|
||||||
|
error "Not implemented yet"
|
||||||
|
|
||||||
|
getClothFollowersR
|
||||||
|
:: KeyHashid Loom -> KeyHashid TicketLoom -> Handler TypedContent
|
||||||
|
getClothFollowersR loomHash clothHash = getFollowersCollection here getFsid
|
||||||
|
where
|
||||||
|
here = ClothFollowersR loomHash clothHash
|
||||||
|
getFsid = do
|
||||||
|
(_, _, Entity _ t, _, _, _) <- getCloth404 loomHash clothHash
|
||||||
|
return $ ticketFollowers t
|
||||||
|
|
||||||
|
getClothDepsR
|
||||||
|
:: KeyHashid Loom -> KeyHashid TicketLoom -> Handler TypedContent
|
||||||
|
getClothDepsR loomHash clothHash =
|
||||||
|
error "Temporarily disabled"
|
||||||
|
{-
|
||||||
|
getDependencyCollection here dep getLocalClothId404
|
||||||
|
where
|
||||||
|
here = ClothDepsR loomHash clothHash
|
||||||
|
dep = ClothDepR loomHash clothHash
|
||||||
|
getLocalClothId404 = do
|
||||||
|
(_, _, Entity ltid _, _, _, _, _) <- getCloth404 dkhid ltkhid
|
||||||
|
return ltid
|
||||||
|
-}
|
||||||
|
|
||||||
|
getClothReverseDepsR
|
||||||
|
:: KeyHashid Loom -> KeyHashid TicketLoom -> Handler TypedContent
|
||||||
|
getClothReverseDepsR loomHash clothHash =
|
||||||
|
error "Temporarily disabled"
|
||||||
|
{-
|
||||||
|
getReverseDependencyCollection here getLocalClothId404
|
||||||
|
where
|
||||||
|
here = ClothReverseDepsR loomhash clothHash
|
||||||
|
getLocalClothId404 = do
|
||||||
|
(_, _, _, Entity ltid _, _, _, _, _) <- getCloth404 loomHash clothHash
|
||||||
|
return ltid
|
||||||
|
-}
|
||||||
|
|
||||||
|
getBundleR
|
||||||
|
:: KeyHashid Loom
|
||||||
|
-> KeyHashid TicketLoom
|
||||||
|
-> KeyHashid Bundle
|
||||||
|
-> Handler TypedContent
|
||||||
|
getBundleR loomHash clothHash bundleHash = do
|
||||||
|
(patchIDs, previousBundles, maybeCurrentBundle) <- runDB $ do
|
||||||
|
(_, Entity clothID _, _, _, _, latest :| prevs) <-
|
||||||
|
getCloth404 loomHash clothHash
|
||||||
|
bundleID <- decodeKeyHashid404 bundleHash
|
||||||
|
bundle <- get404 bundleID
|
||||||
|
unless (bundleTicket bundle == clothID) notFound
|
||||||
|
patches <- do
|
||||||
|
ids <- selectKeysList [PatchBundle ==. bundleID] [Desc PatchId]
|
||||||
|
case nonEmpty ids of
|
||||||
|
Nothing -> error "Bundle without any Patches in DB"
|
||||||
|
Just ne -> return ne
|
||||||
|
let (prevs, mcurr) =
|
||||||
|
if bundleID == latest
|
||||||
|
then (prevs, Nothing)
|
||||||
|
else ([] , Just latest)
|
||||||
|
return (patches, prevs, mcurr)
|
||||||
|
|
||||||
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
|
hashBundle <- getEncodeKeyHashid
|
||||||
|
hashPatch <- getEncodeKeyHashid
|
||||||
|
|
||||||
|
let versionRoute = BundleR loomHash clothHash . hashBundle
|
||||||
|
bundleLocalAP = AP.BundleLocal
|
||||||
|
{ AP.bundleId = encodeRouteLocal here
|
||||||
|
, AP.bundleContext =
|
||||||
|
encodeRouteLocal $ ClothR loomHash clothHash
|
||||||
|
, AP.bundlePrevVersions =
|
||||||
|
map (encodeRouteLocal . versionRoute) previousBundles
|
||||||
|
, AP.bundleCurrentVersion =
|
||||||
|
encodeRouteLocal . versionRoute <$> maybeCurrentBundle
|
||||||
|
}
|
||||||
|
bundleAP =
|
||||||
|
AP.BundleHosted
|
||||||
|
(Just bundleLocalAP)
|
||||||
|
(NE.map
|
||||||
|
( encodeRouteLocal
|
||||||
|
. PatchR loomHash clothHash bundleHash
|
||||||
|
. hashPatch
|
||||||
|
)
|
||||||
|
patchIDs
|
||||||
|
)
|
||||||
|
|
||||||
|
provideHtmlAndAP bundleAP $ redirectToPrettyJSON here
|
||||||
|
where
|
||||||
|
here = BundleR loomHash clothHash bundleHash
|
||||||
|
|
||||||
|
getPatchR
|
||||||
|
:: KeyHashid Loom
|
||||||
|
-> KeyHashid TicketLoom
|
||||||
|
-> KeyHashid Bundle
|
||||||
|
-> KeyHashid Patch
|
||||||
|
-> Handler TypedContent
|
||||||
|
getPatchR loomHash clothHash bundleHash patchHash = do
|
||||||
|
(patch, author) <- runDB $ do
|
||||||
|
(_, _, _, author', _, versions) <- getCloth404 loomHash clothHash
|
||||||
|
(,) <$> do bundleID <- decodeKeyHashid404 bundleHash
|
||||||
|
unless (bundleID `elem` versions) notFound
|
||||||
|
patchID <- decodeKeyHashid404 patchHash
|
||||||
|
patch' <- get404 patchID
|
||||||
|
unless (patchBundle patch' == bundleID) notFound
|
||||||
|
return patch'
|
||||||
|
<*> bitraverse
|
||||||
|
(\ (Entity _ tal) -> return $ ticketAuthorLocalAuthor tal)
|
||||||
|
(\ (Entity _ tar) -> do
|
||||||
|
ra <- getJust $ ticketAuthorRemoteAuthor tar
|
||||||
|
ro <- getJust $ remoteActorIdent ra
|
||||||
|
i <- getJust $ remoteObjectInstance ro
|
||||||
|
return (i, ro)
|
||||||
|
)
|
||||||
|
author'
|
||||||
|
|
||||||
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
|
hashPerson <- getEncodeKeyHashid
|
||||||
|
hLocal <- getsYesod siteInstanceHost
|
||||||
|
|
||||||
|
let host =
|
||||||
|
case author of
|
||||||
|
Left _ -> hLocal
|
||||||
|
Right (i, _) -> instanceHost i
|
||||||
|
patchLocalAP = AP.PatchLocal
|
||||||
|
{ AP.patchId = encodeRouteLocal here
|
||||||
|
, AP.patchContext =
|
||||||
|
encodeRouteLocal $ BundleR loomHash clothHash bundleHash
|
||||||
|
}
|
||||||
|
patchAP = AP.Patch
|
||||||
|
{ AP.patchLocal = Just (hLocal, patchLocalAP)
|
||||||
|
, AP.patchAttributedTo =
|
||||||
|
case author of
|
||||||
|
Left authorID ->
|
||||||
|
encodeRouteLocal $ PersonR $ hashPerson authorID
|
||||||
|
Right (_, object) -> remoteObjectIdent object
|
||||||
|
, AP.patchPublished = Just $ patchCreated patch
|
||||||
|
, AP.patchType = patchType patch
|
||||||
|
, AP.patchContent = patchContent patch
|
||||||
|
}
|
||||||
|
provideHtmlAndAP' host patchAP $ redirectToPrettyJSON here
|
||||||
|
where
|
||||||
|
here = PatchR loomHash clothHash bundleHash patchHash
|
||||||
|
|
||||||
|
getClothDepR
|
||||||
|
:: KeyHashid Loom
|
||||||
|
-> KeyHashid TicketLoom
|
||||||
|
-> KeyHashid LocalTicketDependency
|
||||||
|
-> Handler TypedContent
|
||||||
|
getClothDepR _ _ _ = do
|
||||||
|
error "Temporarily disabled"
|
||||||
|
{-
|
||||||
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
|
encodeRouteHome <- getEncodeRouteHome
|
||||||
|
wiRoute <- askWorkItemRoute
|
||||||
|
hLocal <- asksSite siteInstanceHost
|
||||||
|
|
||||||
|
tdid <- decodeKeyHashid404 tdkhid
|
||||||
|
(td, author, parent, child) <- runDB $ do
|
||||||
|
td <- get404 tdid
|
||||||
|
(td,,,)
|
||||||
|
<$> getAuthor tdid
|
||||||
|
<*> getWorkItem ( localTicketDependencyParent td)
|
||||||
|
<*> getChild tdid
|
||||||
|
let host =
|
||||||
|
case author of
|
||||||
|
Left _ -> hLocal
|
||||||
|
Right (h, _) -> h
|
||||||
|
tdepAP = AP.TicketDependency
|
||||||
|
{ ticketDepId = Just $ encodeRouteHome here
|
||||||
|
, ticketDepParent = encodeRouteHome $ wiRoute parent
|
||||||
|
, ticketDepChild =
|
||||||
|
case child of
|
||||||
|
Left wi -> encodeRouteHome $ wiRoute wi
|
||||||
|
Right (h, lu) -> ObjURI h lu
|
||||||
|
, ticketDepAttributedTo =
|
||||||
|
case author of
|
||||||
|
Left shr -> encodeRouteLocal $ SharerR shr
|
||||||
|
Right (_h, lu) -> lu
|
||||||
|
, ticketDepPublished = Just $ localTicketDependencyCreated td
|
||||||
|
, ticketDepUpdated = Nothing
|
||||||
|
}
|
||||||
|
provideHtmlAndAP' host tdepAP $ redirectToPrettyJSON here
|
||||||
|
where
|
||||||
|
here = TicketDepR tdkhid
|
||||||
|
getAuthor tdid = do
|
||||||
|
tda <- requireEitherAlt
|
||||||
|
(getValBy $ UniqueTicketDependencyAuthorLocal tdid)
|
||||||
|
(getValBy $ UniqueTicketDependencyAuthorRemote tdid)
|
||||||
|
"No TDA"
|
||||||
|
"Both TDAL and TDAR"
|
||||||
|
bitraverse
|
||||||
|
(\ tdal -> do
|
||||||
|
p <- getJust $ ticketDependencyAuthorLocalAuthor tdal
|
||||||
|
s <- getJust $ personIdent p
|
||||||
|
return $ sharerIdent s
|
||||||
|
)
|
||||||
|
(\ tdar -> do
|
||||||
|
ra <- getJust $ ticketDependencyAuthorRemoteAuthor tdar
|
||||||
|
ro <- getJust $ remoteActorIdent ra
|
||||||
|
i <- getJust $ remoteObjectInstance ro
|
||||||
|
return (instanceHost i, remoteObjectIdent ro)
|
||||||
|
)
|
||||||
|
tda
|
||||||
|
getChild tdid = do
|
||||||
|
tdc <- requireEitherAlt
|
||||||
|
(getValBy $ UniqueTicketDependencyChildLocal tdid)
|
||||||
|
(getValBy $ UniqueTicketDependencyChildRemote tdid)
|
||||||
|
"No TDC"
|
||||||
|
"Both TDCL and TDCR"
|
||||||
|
bitraverse
|
||||||
|
(getWorkItem . ticketDependencyChildLocalChild)
|
||||||
|
(\ tdcr -> do
|
||||||
|
ro <- getJust $ ticketDependencyChildRemoteChild tdcr
|
||||||
|
i <- getJust $ remoteObjectInstance ro
|
||||||
|
return (instanceHost i, remoteObjectIdent ro)
|
||||||
|
)
|
||||||
|
tdc
|
||||||
|
-}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{-
|
||||||
getSharerProposalsR :: ShrIdent -> Handler TypedContent
|
getSharerProposalsR :: ShrIdent -> Handler TypedContent
|
||||||
getSharerProposalsR =
|
getSharerProposalsR =
|
||||||
getSharerWorkItems SharerProposalsR SharerProposalR countPatches selectPatches
|
getSharerWorkItems SharerProposalsR SharerProposalR countPatches selectPatches
|
||||||
|
@ -595,112 +1028,4 @@ getRepoProposalEventsR shr rp ltkhid = do
|
||||||
provideEmptyCollection
|
provideEmptyCollection
|
||||||
CollectionTypeOrdered
|
CollectionTypeOrdered
|
||||||
(RepoProposalEventsR shr rp ltkhid)
|
(RepoProposalEventsR shr rp ltkhid)
|
||||||
|
-}
|
||||||
getRepoProposalBundleR
|
|
||||||
:: ShrIdent
|
|
||||||
-> RpIdent
|
|
||||||
-> KeyHashid LocalTicket
|
|
||||||
-> KeyHashid Bundle
|
|
||||||
-> Handler TypedContent
|
|
||||||
getRepoProposalBundleR shr rp ltkhid bnkhid = do
|
|
||||||
(ptids, prevs, mcurr) <- runDB $ do
|
|
||||||
(_, _, Entity tid _, _, _, _, _, _, v :| vs) <- getRepoProposal404 shr rp ltkhid
|
|
||||||
bnid <- decodeKeyHashid404 bnkhid
|
|
||||||
bn <- get404 bnid
|
|
||||||
unless (bundleTicket bn == tid) notFound
|
|
||||||
ptids <- selectKeysList [PatchBundle ==. bnid] [Desc PatchId]
|
|
||||||
ptidsNE <-
|
|
||||||
case nonEmpty ptids of
|
|
||||||
Nothing -> error "Bundle without any Patches in DB"
|
|
||||||
Just ne -> return ne
|
|
||||||
let (prevs, mcurr) =
|
|
||||||
if bnid == v
|
|
||||||
then (vs, Nothing)
|
|
||||||
else ([], Just v)
|
|
||||||
return (ptidsNE, prevs, mcurr)
|
|
||||||
|
|
||||||
encodeRouteLocal <- getEncodeRouteLocal
|
|
||||||
encodeBNID <- getEncodeKeyHashid
|
|
||||||
encodePTID <- getEncodeKeyHashid
|
|
||||||
|
|
||||||
let versionRoute = RepoProposalBundleR shr rp ltkhid . encodeBNID
|
|
||||||
local = BundleLocal
|
|
||||||
{ bundleId = encodeRouteLocal here
|
|
||||||
, bundleContext =
|
|
||||||
encodeRouteLocal $ RepoProposalR shr rp ltkhid
|
|
||||||
, bundlePrevVersions =
|
|
||||||
map (encodeRouteLocal . versionRoute) prevs
|
|
||||||
, bundleCurrentVersion = encodeRouteLocal . versionRoute <$> mcurr
|
|
||||||
}
|
|
||||||
bundleAP =
|
|
||||||
AP.BundleHosted
|
|
||||||
(Just local)
|
|
||||||
(NE.map
|
|
||||||
( encodeRouteLocal
|
|
||||||
. RepoProposalBundlePatchR shr rp ltkhid bnkhid
|
|
||||||
. encodePTID
|
|
||||||
)
|
|
||||||
ptids
|
|
||||||
)
|
|
||||||
provideHtmlAndAP bundleAP $ redirectToPrettyJSON here
|
|
||||||
where
|
|
||||||
here = RepoProposalBundleR shr rp ltkhid bnkhid
|
|
||||||
|
|
||||||
getRepoProposalBundlePatchR
|
|
||||||
:: ShrIdent
|
|
||||||
-> RpIdent
|
|
||||||
-> KeyHashid LocalTicket
|
|
||||||
-> KeyHashid Bundle
|
|
||||||
-> KeyHashid Patch
|
|
||||||
-> Handler TypedContent
|
|
||||||
getRepoProposalBundlePatchR shr rp ltkhid bnkhid ptkhid = do
|
|
||||||
(patch, author) <- runDB $ do
|
|
||||||
(_, _, _, _, _, _, ta, _, vers) <- getRepoProposal404 shr rp ltkhid
|
|
||||||
(,) <$> do bnid <- decodeKeyHashid404 bnkhid
|
|
||||||
unless (bnid `elem` vers) notFound
|
|
||||||
ptid <- decodeKeyHashid404 ptkhid
|
|
||||||
pt <- get404 ptid
|
|
||||||
unless (patchBundle pt == bnid) notFound
|
|
||||||
return pt
|
|
||||||
<*> bitraverse
|
|
||||||
(\ (Entity _ tal, _) -> do
|
|
||||||
p <- getJust $ ticketAuthorLocalAuthor tal
|
|
||||||
getJust $ personIdent p
|
|
||||||
)
|
|
||||||
(\ (Entity _ tar) -> do
|
|
||||||
ra <- getJust $ ticketAuthorRemoteAuthor tar
|
|
||||||
ro <- getJust $ remoteActorIdent ra
|
|
||||||
i <- getJust $ remoteObjectInstance ro
|
|
||||||
return (i, ro)
|
|
||||||
)
|
|
||||||
ta
|
|
||||||
|
|
||||||
encodeRouteLocal <- getEncodeRouteLocal
|
|
||||||
hLocal <- getsYesod siteInstanceHost
|
|
||||||
|
|
||||||
let host =
|
|
||||||
case author of
|
|
||||||
Left _ -> hLocal
|
|
||||||
Right (i, _) -> instanceHost i
|
|
||||||
patchAP = AP.Patch
|
|
||||||
{ AP.patchLocal = Just
|
|
||||||
( hLocal
|
|
||||||
, AP.PatchLocal
|
|
||||||
{ AP.patchId = encodeRouteLocal here
|
|
||||||
, AP.patchContext =
|
|
||||||
encodeRouteLocal $
|
|
||||||
RepoProposalBundleR shr rp ltkhid bnkhid
|
|
||||||
}
|
|
||||||
)
|
|
||||||
, AP.patchAttributedTo =
|
|
||||||
case author of
|
|
||||||
Left sharer ->
|
|
||||||
encodeRouteLocal $ SharerR $ sharerIdent sharer
|
|
||||||
Right (_, object) -> remoteObjectIdent object
|
|
||||||
, AP.patchPublished = Just $ patchCreated patch
|
|
||||||
, AP.patchType = patchType patch
|
|
||||||
, AP.patchContent = patchContent patch
|
|
||||||
}
|
|
||||||
provideHtmlAndAP' host patchAP $ redirectToPrettyJSON here
|
|
||||||
where
|
|
||||||
here = RepoProposalBundlePatchR shr rp ltkhid bnkhid ptkhid
|
|
|
@ -13,14 +13,35 @@
|
||||||
- <http://creativecommons.org/publicdomain/zero/1.0/>.
|
- <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
-}
|
-}
|
||||||
|
|
||||||
module Vervis.Handler.Project
|
module Vervis.Handler.Deck
|
||||||
( getProjectsR
|
( getDeckR
|
||||||
, postProjectsR
|
, getDeckInboxR
|
||||||
, getProjectNewR
|
, postDeckInboxR
|
||||||
|
, getDeckOutboxR
|
||||||
|
, getDeckOutboxItemR
|
||||||
|
, getDeckFollowersR
|
||||||
|
, getDeckTicketsR
|
||||||
|
|
||||||
|
, getDeckTreeR
|
||||||
|
|
||||||
|
, getDeckNewR
|
||||||
|
, postDeckNewR
|
||||||
|
, postDeckDeleteR
|
||||||
|
, getDeckEditR
|
||||||
|
, postDeckEditR
|
||||||
|
, postDeckFollowR
|
||||||
|
, postDeckUnfollowR
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{-
|
||||||
|
, getProjectsR
|
||||||
, getProjectR
|
, getProjectR
|
||||||
, putProjectR
|
, putProjectR
|
||||||
, postProjectR
|
|
||||||
, getProjectEditR
|
|
||||||
, getProjectDevsR
|
, getProjectDevsR
|
||||||
, postProjectDevsR
|
, postProjectDevsR
|
||||||
, getProjectDevNewR
|
, getProjectDevNewR
|
||||||
|
@ -28,19 +49,19 @@ module Vervis.Handler.Project
|
||||||
, deleteProjectDevR
|
, deleteProjectDevR
|
||||||
, postProjectDevR
|
, postProjectDevR
|
||||||
, getProjectTeamR
|
, getProjectTeamR
|
||||||
, getProjectFollowersR
|
-}
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
import Control.Monad.Trans.Except
|
import Control.Monad.Trans.Except
|
||||||
|
import Data.Aeson
|
||||||
import Data.Foldable
|
import Data.Foldable
|
||||||
import Data.Maybe (fromMaybe)
|
import Data.Maybe (fromMaybe)
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
import Data.Time.Clock
|
import Data.Time.Clock
|
||||||
import Data.Traversable
|
import Data.Traversable
|
||||||
import Database.Persist
|
import Database.Persist
|
||||||
import Database.Esqueleto hiding (delete, (%), (==.))
|
|
||||||
import Text.Blaze.Html (Html)
|
import Text.Blaze.Html (Html)
|
||||||
import Yesod.Auth (requireAuth)
|
import Yesod.Auth (requireAuth)
|
||||||
import Yesod.Core
|
import Yesod.Core
|
||||||
|
@ -49,47 +70,198 @@ import Yesod.Form.Functions (runFormPost)
|
||||||
import Yesod.Form.Types (FormResult (..))
|
import Yesod.Form.Types (FormResult (..))
|
||||||
import Yesod.Persist.Core (runDB, get404, getBy404)
|
import Yesod.Persist.Core (runDB, get404, getBy404)
|
||||||
|
|
||||||
|
import qualified Data.ByteString.Lazy as BL
|
||||||
import qualified Database.Esqueleto as E
|
import qualified Database.Esqueleto as E
|
||||||
|
|
||||||
import Database.Persist.JSON
|
import Database.Persist.JSON
|
||||||
|
import Development.PatchMediaType
|
||||||
import Network.FedURI
|
import Network.FedURI
|
||||||
import Web.ActivityPub hiding (Project (..), Repo (..), Actor (..), ActorDetail (..), ActorLocal (..))
|
import Web.ActivityPub hiding (Project (..), Repo (..), Actor (..), ActorDetail (..), ActorLocal (..))
|
||||||
import Yesod.ActivityPub
|
import Yesod.ActivityPub
|
||||||
import Yesod.FedURI
|
import Yesod.FedURI
|
||||||
|
import Yesod.Hashids
|
||||||
import Yesod.MonadSite
|
import Yesod.MonadSite
|
||||||
|
|
||||||
import qualified Web.ActivityPub as AP
|
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.Paginate.Local
|
||||||
import Database.Persist.Local
|
import Database.Persist.Local
|
||||||
import Yesod.Persist.Local
|
import Yesod.Persist.Local
|
||||||
|
|
||||||
|
import Vervis.Actor
|
||||||
import Vervis.API
|
import Vervis.API
|
||||||
import Vervis.Client
|
|
||||||
import Vervis.Federation
|
import Vervis.Federation
|
||||||
import Vervis.Form.Project
|
|
||||||
import Vervis.Foundation
|
import Vervis.Foundation
|
||||||
import Vervis.Model
|
import Vervis.Model
|
||||||
import Vervis.Model.Ident
|
import Vervis.Paginate
|
||||||
import Development.PatchMediaType
|
|
||||||
import Vervis.Settings
|
import Vervis.Settings
|
||||||
import Vervis.Widget.Project
|
import Vervis.Widget.Person
|
||||||
import Vervis.Widget.Sharer
|
|
||||||
import Vervis.Widget.Workflow
|
|
||||||
|
|
||||||
getProjectsR :: ShrIdent -> Handler Html
|
getDeckR :: KeyHashid Deck -> Handler TypedContent
|
||||||
getProjectsR ident = do
|
getDeckR deckHash = do
|
||||||
projects <- runDB $ select $ from $ \ (sharer, project) -> do
|
deckID <- decodeKeyHashid404 deckHash
|
||||||
where_ $
|
(deck, repoIDs, actor) <- runDB $ do
|
||||||
sharer ^. SharerIdent E.==. val ident &&.
|
d <- get404 deckID
|
||||||
sharer ^. SharerId E.==. project ^. ProjectSharer
|
rs <- selectKeysList [RepoProject ==. Just deckID] [Asc RepoId]
|
||||||
orderBy [asc $ project ^. ProjectIdent]
|
(d,rs,) <$> getJust (deckActor d)
|
||||||
return $ project ^. ProjectIdent
|
|
||||||
defaultLayout $(widgetFile "project/list")
|
|
||||||
|
|
||||||
postProjectsR :: ShrIdent -> Handler Html
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
postProjectsR shr = do
|
let deckAP = AP.TicketTracker
|
||||||
|
{ AP.ticketTrackerActor = AP.Actor
|
||||||
|
{ AP.actorLocal = AP.ActorLocal
|
||||||
|
{ AP.actorId = encodeRouteLocal $ DeckR deckHash
|
||||||
|
, AP.actorInbox = encodeRouteLocal $ DeckInboxR deckHash
|
||||||
|
, AP.actorOutbox =
|
||||||
|
Just $ encodeRouteLocal $ DeckOutboxR deckHash
|
||||||
|
, AP.actorFollowers =
|
||||||
|
Just $ encodeRouteLocal $ DeckFollowersR deckHash
|
||||||
|
, AP.actorFollowing = Nothing
|
||||||
|
, AP.actorPublicKeys =
|
||||||
|
[ Left $ encodeRouteLocal ActorKey1R
|
||||||
|
, Left $ encodeRouteLocal ActorKey2R
|
||||||
|
]
|
||||||
|
, AP.actorSshKeys = []
|
||||||
|
}
|
||||||
|
, AP.actorDetail = AP.ActorDetail
|
||||||
|
{ AP.actorType = ActorTypeTicketTracker
|
||||||
|
, AP.actorUsername = Nothing
|
||||||
|
, AP.actorName = Just $ actorName actor
|
||||||
|
, AP.actorSummary = Just $ actorDesc actor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
, AP.ticketTrackerTeam = Nothing
|
||||||
|
}
|
||||||
|
followButton =
|
||||||
|
followW
|
||||||
|
(DeckFollowR deckHash)
|
||||||
|
(DeckUnfollowR deckHash)
|
||||||
|
(actorFollowers actor)
|
||||||
|
|
||||||
|
provideHtmlAndAP deckAP $ redirectToPrettyJSON $ DeckR deckHash
|
||||||
|
|
||||||
|
getDeckInboxR :: KeyHashid Deck -> Handler TypedContent
|
||||||
|
getDeckInboxR = getInbox DeckInboxR deckActor
|
||||||
|
|
||||||
|
postDeckInboxR :: KeyHashid Deck -> Handler TypedContent
|
||||||
|
postDeckInboxR _ = error "Temporarily disabled"
|
||||||
|
|
||||||
|
getDeckOutboxR :: KeyHashid Deck -> Handler TypedContent
|
||||||
|
getDeckOutboxR = getOutbox DeckOutboxR deckActor
|
||||||
|
|
||||||
|
getDeckOutboxItemR
|
||||||
|
:: KeyHashid Deck -> KeyHashid OutboxItem -> Handler TypedContent
|
||||||
|
getDeckOutboxItemR = getOutboxItem DeckOutboxItemR deckActor
|
||||||
|
|
||||||
|
getDeckFollowersR :: KeyHashid Deck -> Handler TypedContent
|
||||||
|
getDeckFollowersR = getActorFollowersCollection DeckFollowersR deckActor
|
||||||
|
|
||||||
|
getDeckTicketsR :: KeyHashid Deck -> Handler TypedContent
|
||||||
|
getDeckTicketsR deckHash = selectRep $ do
|
||||||
|
{-
|
||||||
|
provideRep $ do
|
||||||
|
((filtResult, filtWidget), filtEnctype) <- runFormGet ticketFilterForm
|
||||||
|
let tf =
|
||||||
|
case filtResult of
|
||||||
|
FormSuccess filt -> filt
|
||||||
|
FormMissing -> def
|
||||||
|
FormFailure l ->
|
||||||
|
error $ "Ticket filter form failed: " ++ show l
|
||||||
|
(total, pages, mpage) <- runDB $ do
|
||||||
|
Entity sid _ <- getBy404 $ UniqueSharer shr
|
||||||
|
Entity jid _ <- getBy404 $ UniqueProject prj sid
|
||||||
|
let countAllTickets = count [TicketProjectLocalProject ==. jid]
|
||||||
|
selectTickets off lim =
|
||||||
|
getTicketSummaries
|
||||||
|
(filterTickets tf)
|
||||||
|
(Just $ \ t -> [E.asc $ t E.^. TicketId])
|
||||||
|
(Just (off, lim))
|
||||||
|
jid
|
||||||
|
getPageAndNavCount countAllTickets selectTickets
|
||||||
|
case mpage of
|
||||||
|
Nothing -> redirectFirstPage here
|
||||||
|
Just (rows, navModel) ->
|
||||||
|
let pageNav = navWidget navModel
|
||||||
|
in defaultLayout $(widgetFile "ticket/list")
|
||||||
|
-}
|
||||||
|
provideAP' $ do
|
||||||
|
deckID <- decodeKeyHashid404 deckHash
|
||||||
|
(total, pages, mpage) <- runDB $ do
|
||||||
|
let countAllTickets = count [TicketDeckDeck ==. deckID]
|
||||||
|
selectTickets off lim =
|
||||||
|
selectKeysList
|
||||||
|
[TicketDeckDeck ==. deckID]
|
||||||
|
[OffsetBy off, LimitTo lim, Desc TicketDeckTicket]
|
||||||
|
getPageAndNavCount countAllTickets selectTickets
|
||||||
|
|
||||||
|
encodeRouteHome <- getEncodeRouteHome
|
||||||
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
|
hashTicket <- getEncodeKeyHashid
|
||||||
|
encodeRoutePageLocal <- getEncodeRoutePageLocal
|
||||||
|
let pageUrl = encodeRoutePageLocal here
|
||||||
|
host <- asksSite siteInstanceHost
|
||||||
|
return $
|
||||||
|
case mpage of
|
||||||
|
Nothing -> encodeStrict $ Doc host $ Collection
|
||||||
|
{ collectionId = encodeRouteLocal here
|
||||||
|
, collectionType = CollectionTypeOrdered
|
||||||
|
, collectionTotalItems = Just total
|
||||||
|
, collectionCurrent = Nothing
|
||||||
|
, collectionFirst = Just $ pageUrl 1
|
||||||
|
, collectionLast = Just $ pageUrl pages
|
||||||
|
, collectionItems = [] :: [Text]
|
||||||
|
}
|
||||||
|
Just (tickets, navModel) ->
|
||||||
|
let current = nmCurrent navModel
|
||||||
|
in encodeStrict $ Doc host $ 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 =
|
||||||
|
encodeRouteHome . TicketR deckHash . hashTicket <$> tickets
|
||||||
|
}
|
||||||
|
where
|
||||||
|
here = DeckTicketsR deckHash
|
||||||
|
encodeStrict = BL.toStrict . encode
|
||||||
|
|
||||||
|
getDeckTreeR :: KeyHashid Deck -> Handler Html
|
||||||
|
getDeckTreeR _ = error "Temporarily disabled"
|
||||||
|
{-
|
||||||
|
(summaries, deps) <- runDB $ do
|
||||||
|
Entity sid _ <- getBy404 $ UniqueSharer shr
|
||||||
|
Entity jid _ <- getBy404 $ UniqueProject prj sid
|
||||||
|
(,) <$> getTicketSummaries Nothing Nothing Nothing jid
|
||||||
|
<*> getTicketDepEdges jid
|
||||||
|
defaultLayout $ ticketTreeDW shr prj summaries deps
|
||||||
|
-}
|
||||||
|
|
||||||
|
getDeckNewR :: Handler Html
|
||||||
|
getDeckNewR = do
|
||||||
|
error "Temporarily disabled"
|
||||||
|
{-
|
||||||
|
Entity sid _ <- runDB $ getBy404 $ UniqueSharer shr
|
||||||
|
((_result, widget), enctype) <- runFormPost $ newProjectForm sid
|
||||||
|
defaultLayout $(widgetFile "project/new")
|
||||||
|
-}
|
||||||
|
|
||||||
|
postDeckNewR :: Handler Html
|
||||||
|
postDeckNewR = do
|
||||||
|
error "Temporarily disabled"
|
||||||
|
{-
|
||||||
ep@(Entity _ p) <- requireAuth
|
ep@(Entity _ p) <- requireAuth
|
||||||
Entity sid s <- runDB $ do
|
Entity sid s <- runDB $ do
|
||||||
_ <- getBy404 $ UniqueSharer shr
|
_ <- getBy404 $ UniqueSharer shr
|
||||||
|
@ -115,64 +287,27 @@ postProjectsR shr = do
|
||||||
Right prj -> do
|
Right prj -> do
|
||||||
setMessage "Project created!"
|
setMessage "Project created!"
|
||||||
redirect $ ProjectR shr prj
|
redirect $ ProjectR shr prj
|
||||||
|
-}
|
||||||
|
|
||||||
getProjectNewR :: ShrIdent -> Handler Html
|
postDeckDeleteR :: KeyHashid Deck -> Handler Html
|
||||||
getProjectNewR shr = do
|
postDeckDeleteR _ = error "Temporarily disabled"
|
||||||
Entity sid _ <- runDB $ getBy404 $ UniqueSharer shr
|
|
||||||
((_result, widget), enctype) <- runFormPost $ newProjectForm sid
|
|
||||||
defaultLayout $(widgetFile "project/new")
|
|
||||||
|
|
||||||
getProjectR :: ShrIdent -> PrjIdent -> Handler TypedContent
|
getDeckEditR :: KeyHashid Deck -> Handler Html
|
||||||
getProjectR shar proj = do
|
getDeckEditR _ = do
|
||||||
(actor, project, workflow, wsharer, repos) <- runDB $ do
|
error "Temporarily disabled"
|
||||||
Entity sid s <- getBy404 $ UniqueSharer shar
|
{-
|
||||||
Entity pid p <- getBy404 $ UniqueProject proj sid
|
(sid, ep) <- runDB $ do
|
||||||
w <- get404 $ projectWorkflow p
|
Entity sid _sharer <- getBy404 $ UniqueSharer shr
|
||||||
sw <-
|
ep <- getBy404 $ UniqueProject prj sid
|
||||||
if workflowSharer w == sid
|
return (sid, ep)
|
||||||
then return s
|
((_result, widget), enctype) <- runFormPost $ editProjectForm sid ep
|
||||||
else get404 $ workflowSharer w
|
defaultLayout $(widgetFile "project/edit")
|
||||||
rs <- selectList [RepoProject ==. Just pid] [Asc RepoIdent]
|
-}
|
||||||
a <- getJust $ projectActor p
|
|
||||||
return (a, p, w, sw, rs)
|
|
||||||
|
|
||||||
route2fed <- getEncodeRouteHome
|
postDeckEditR :: KeyHashid Deck -> Handler Html
|
||||||
route2local <- getEncodeRouteLocal
|
postDeckEditR _ = do
|
||||||
let projectAP = AP.TicketTracker
|
error "Temporarily disabled"
|
||||||
{ AP.ticketTrackerActor = AP.Actor
|
{-
|
||||||
{ AP.actorLocal = AP.ActorLocal
|
|
||||||
{ AP.actorId = route2local $ ProjectR shar proj
|
|
||||||
, AP.actorInbox = route2local $ ProjectInboxR shar proj
|
|
||||||
, AP.actorOutbox =
|
|
||||||
Just $ route2local $ ProjectOutboxR shar proj
|
|
||||||
, AP.actorFollowers =
|
|
||||||
Just $ route2local $ ProjectFollowersR shar proj
|
|
||||||
, AP.actorFollowing = Nothing
|
|
||||||
, AP.actorPublicKeys =
|
|
||||||
[ Left $ route2local ActorKey1R
|
|
||||||
, Left $ route2local ActorKey2R
|
|
||||||
]
|
|
||||||
, AP.actorSshKeys = []
|
|
||||||
}
|
|
||||||
, AP.actorDetail = AP.ActorDetail
|
|
||||||
{ AP.actorType = ActorTypeTicketTracker
|
|
||||||
, AP.actorUsername = Nothing
|
|
||||||
, AP.actorName =
|
|
||||||
Just $ fromMaybe (prj2text proj) $ projectName project
|
|
||||||
, AP.actorSummary = projectDesc project
|
|
||||||
}
|
|
||||||
}
|
|
||||||
, AP.ticketTrackerTeam = route2local $ ProjectTeamR shar proj
|
|
||||||
}
|
|
||||||
followButton =
|
|
||||||
followW
|
|
||||||
(ProjectFollowR shar proj)
|
|
||||||
(ProjectUnfollowR shar proj)
|
|
||||||
(return $ actorFollowers actor)
|
|
||||||
provideHtmlAndAP projectAP $(widgetFile "project/one")
|
|
||||||
|
|
||||||
putProjectR :: ShrIdent -> PrjIdent -> Handler Html
|
|
||||||
putProjectR shr prj = do
|
|
||||||
(sid, ep@(Entity jid _)) <- runDB $ do
|
(sid, ep@(Entity jid _)) <- runDB $ do
|
||||||
Entity sid _sharer <- getBy404 $ UniqueSharer shr
|
Entity sid _sharer <- getBy404 $ UniqueSharer shr
|
||||||
eproj <- getBy404 $ UniqueProject prj sid
|
eproj <- getBy404 $ UniqueProject prj sid
|
||||||
|
@ -189,22 +324,50 @@ putProjectR shr prj = do
|
||||||
FormFailure _l -> do
|
FormFailure _l -> do
|
||||||
setMessage "Project update failed, see errors below."
|
setMessage "Project update failed, see errors below."
|
||||||
defaultLayout $(widgetFile "project/edit")
|
defaultLayout $(widgetFile "project/edit")
|
||||||
|
-}
|
||||||
|
|
||||||
postProjectR :: ShrIdent -> PrjIdent -> Handler Html
|
postDeckFollowR :: KeyHashid Deck -> Handler ()
|
||||||
postProjectR shr prj = do
|
postDeckFollowR _ = error "Temporarily disabled"
|
||||||
mmethod <- lookupPostParam "_method"
|
|
||||||
case mmethod of
|
|
||||||
Just "PUT" -> putProjectR shr prj
|
|
||||||
_ -> notFound
|
|
||||||
|
|
||||||
getProjectEditR :: ShrIdent -> PrjIdent -> Handler Html
|
postDeckUnfollowR :: KeyHashid Deck -> Handler ()
|
||||||
getProjectEditR shr prj = do
|
postDeckUnfollowR _ = error "Temporarily disabled"
|
||||||
(sid, ep) <- runDB $ do
|
|
||||||
Entity sid _sharer <- getBy404 $ UniqueSharer shr
|
|
||||||
ep <- getBy404 $ UniqueProject prj sid
|
|
||||||
return (sid, ep)
|
|
||||||
((_result, widget), enctype) <- runFormPost $ editProjectForm sid ep
|
|
||||||
defaultLayout $(widgetFile "project/edit")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{-
|
||||||
|
getProjectsR :: ShrIdent -> Handler Html
|
||||||
|
getProjectsR ident = do
|
||||||
|
projects <- runDB $ select $ from $ \ (sharer, project) -> do
|
||||||
|
where_ $
|
||||||
|
sharer ^. SharerIdent E.==. val ident &&.
|
||||||
|
sharer ^. SharerId E.==. project ^. ProjectSharer
|
||||||
|
orderBy [asc $ project ^. ProjectIdent]
|
||||||
|
return $ project ^. ProjectIdent
|
||||||
|
defaultLayout $(widgetFile "project/list")
|
||||||
|
|
||||||
getProjectDevsR :: ShrIdent -> PrjIdent -> Handler Html
|
getProjectDevsR :: ShrIdent -> PrjIdent -> Handler Html
|
||||||
getProjectDevsR shr prj = do
|
getProjectDevsR shr prj = do
|
||||||
|
@ -371,13 +534,4 @@ getProjectTeamR shr prj = do
|
||||||
, collectionItems = map (encodeRouteHome . SharerR) memberShrs
|
, collectionItems = map (encodeRouteHome . SharerR) memberShrs
|
||||||
}
|
}
|
||||||
provideHtmlAndAP team $ redirect (here, [("prettyjson", "true")])
|
provideHtmlAndAP team $ redirect (here, [("prettyjson", "true")])
|
||||||
|
-}
|
||||||
getProjectFollowersR :: ShrIdent -> PrjIdent -> Handler TypedContent
|
|
||||||
getProjectFollowersR shr prj = getFollowersCollection here getFsid
|
|
||||||
where
|
|
||||||
here = ProjectFollowersR shr prj
|
|
||||||
getFsid = do
|
|
||||||
sid <- getKeyBy404 $ UniqueSharer shr
|
|
||||||
j <- getValBy404 $ UniqueProject prj sid
|
|
||||||
a <- getJust $ projectActor j
|
|
||||||
return $ actorFollowers a
|
|
|
@ -15,11 +15,10 @@
|
||||||
|
|
||||||
module Vervis.Handler.Discussion
|
module Vervis.Handler.Discussion
|
||||||
( getDiscussion
|
( getDiscussion
|
||||||
, getDiscussionMessage
|
--, getTopReply
|
||||||
, getTopReply
|
--, postTopReply
|
||||||
, postTopReply
|
--, getReply
|
||||||
, getReply
|
--, postReply
|
||||||
, postReply
|
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
|
@ -57,7 +56,6 @@ import Database.Persist.Local
|
||||||
import Yesod.Persist.Local
|
import Yesod.Persist.Local
|
||||||
|
|
||||||
import Vervis.API
|
import Vervis.API
|
||||||
import Vervis.Client
|
|
||||||
import Vervis.Discussion
|
import Vervis.Discussion
|
||||||
import Vervis.Federation
|
import Vervis.Federation
|
||||||
import Vervis.FedURI
|
import Vervis.FedURI
|
||||||
|
@ -69,8 +67,6 @@ import Yesod.RenderSource
|
||||||
import Vervis.Settings
|
import Vervis.Settings
|
||||||
import Vervis.Widget.Discussion
|
import Vervis.Widget.Discussion
|
||||||
|
|
||||||
import qualified Vervis.Client as C
|
|
||||||
|
|
||||||
getDiscussion
|
getDiscussion
|
||||||
:: (MessageId -> Route App)
|
:: (MessageId -> Route App)
|
||||||
-> Route App
|
-> Route App
|
||||||
|
@ -79,6 +75,7 @@ getDiscussion
|
||||||
getDiscussion reply topic getdid =
|
getDiscussion reply topic getdid =
|
||||||
defaultLayout $ discussionW getdid topic reply
|
defaultLayout $ discussionW getdid topic reply
|
||||||
|
|
||||||
|
{-
|
||||||
getNode :: AppDB DiscussionId -> MessageId -> AppDB MessageTreeNode
|
getNode :: AppDB DiscussionId -> MessageId -> AppDB MessageTreeNode
|
||||||
getNode getdid mid = do
|
getNode getdid mid = do
|
||||||
did <- getdid
|
did <- getdid
|
||||||
|
@ -119,83 +116,6 @@ getNodeL getdid lmid = do
|
||||||
return $ MessageTreeNode mid m $ MessageTreeNodeLocal lmid s
|
return $ MessageTreeNode mid m $ MessageTreeNodeLocal lmid s
|
||||||
-}
|
-}
|
||||||
|
|
||||||
getDiscussionMessage :: ShrIdent -> LocalMessageId -> Handler TypedContent
|
|
||||||
getDiscussionMessage shr lmid = do
|
|
||||||
doc <- runDB $ do
|
|
||||||
sid <- getKeyBy404 $ UniqueSharer shr
|
|
||||||
pid <- getKeyBy404 $ UniquePersonIdent sid
|
|
||||||
lm <- get404 lmid
|
|
||||||
unless (localMessageAuthor lm == pid) notFound
|
|
||||||
m <- getJust $ localMessageRest lm
|
|
||||||
route2fed <- getEncodeRouteHome
|
|
||||||
uContext <- do
|
|
||||||
let did = messageRoot m
|
|
||||||
mlt <- getBy $ UniqueLocalTicketDiscussion did
|
|
||||||
mrd <- getValBy $ UniqueRemoteDiscussion did
|
|
||||||
case (mlt, mrd) of
|
|
||||||
(Nothing, Nothing) -> fail $ "DiscussionId #" ++ show did ++ " has no context"
|
|
||||||
(Just _, Just _) -> fail $ "DiscussionId #" ++ show did ++ " has both ticket and remote contexts"
|
|
||||||
(Just (Entity ltid lt), Nothing) -> do
|
|
||||||
tpl <- do
|
|
||||||
mtpl <- runMaybeT $ do
|
|
||||||
tclid <- MaybeT $ getKeyBy $ UniqueTicketContextLocal $ localTicketTicket lt
|
|
||||||
MaybeT $ getValBy $ UniqueTicketProjectLocal tclid
|
|
||||||
case mtpl of
|
|
||||||
Nothing -> error "No TPL"
|
|
||||||
Just v -> return v
|
|
||||||
j <- getJust $ ticketProjectLocalProject tpl
|
|
||||||
s <- getJust $ projectSharer j
|
|
||||||
let shr = sharerIdent s
|
|
||||||
prj = projectIdent j
|
|
||||||
ltkhid <- encodeKeyHashid ltid
|
|
||||||
return $ route2fed $ ProjectTicketR shr prj ltkhid
|
|
||||||
(Nothing, Just rd) -> do
|
|
||||||
ro <- getJust $ remoteDiscussionIdent rd
|
|
||||||
i <- getJust $ remoteObjectInstance ro
|
|
||||||
return $ ObjURI (instanceHost i) (remoteObjectIdent ro)
|
|
||||||
muParent <- for (messageParent m) $ \ midParent -> do
|
|
||||||
mlocal <- getBy $ UniqueLocalMessage midParent
|
|
||||||
mremote <- getValBy $ UniqueRemoteMessage midParent
|
|
||||||
case (mlocal, mremote) of
|
|
||||||
(Nothing, Nothing) -> fail "Message with no author"
|
|
||||||
(Just _, Just _) -> fail "Message used as both local and remote"
|
|
||||||
(Just (Entity lmidParent lmParent), Nothing) -> do
|
|
||||||
p <- getJust $ localMessageAuthor lmParent
|
|
||||||
s <- getJust $ personIdent p
|
|
||||||
lmhidParent <- encodeKeyHashid lmidParent
|
|
||||||
return $ route2fed $ MessageR (sharerIdent s) lmhidParent
|
|
||||||
(Nothing, Just rmParent) -> do
|
|
||||||
rs <- getJust $ remoteMessageAuthor rmParent
|
|
||||||
ro <- getJust $ remoteActorIdent rs
|
|
||||||
i <- getJust $ remoteObjectInstance ro
|
|
||||||
return $ ObjURI (instanceHost i) (remoteObjectIdent ro)
|
|
||||||
--ob <- getJust $ localMessageCreate lm
|
|
||||||
--let activity = docValue $ persistJSONValue $ outboxItemActivity ob
|
|
||||||
|
|
||||||
host <- getsYesod $ appInstanceHost . appSettings
|
|
||||||
route2local <- getEncodeRouteLocal
|
|
||||||
lmhid <- encodeKeyHashid lmid
|
|
||||||
return $ Doc host Note
|
|
||||||
{ noteId = Just $ route2local $ MessageR shr lmhid
|
|
||||||
, noteAttrib = route2local $ SharerR shr
|
|
||||||
, noteAudience = Audience [] [] [] [] [] []
|
|
||||||
--case activitySpecific activity of
|
|
||||||
-- CreateActivity (Create note) -> noteAudience note
|
|
||||||
-- _ -> error $ "lmid#" ++ show (fromSqlKey lmid) ++ "'s create isn't a Create activity!"
|
|
||||||
, noteReplyTo = Just $ fromMaybe uContext muParent
|
|
||||||
, noteContext = Just uContext
|
|
||||||
, notePublished = Just $ messageCreated m
|
|
||||||
, noteSource = messageSource m
|
|
||||||
, noteContent = messageContent m
|
|
||||||
}
|
|
||||||
selectRep $ do
|
|
||||||
provideAP $ pure doc
|
|
||||||
provideRep $
|
|
||||||
defaultLayout
|
|
||||||
[whamlet|
|
|
||||||
<div><pre>#{encodePrettyToLazyText doc}
|
|
||||||
|]
|
|
||||||
|
|
||||||
getTopReply :: Route App -> Handler Html
|
getTopReply :: Route App -> Handler Html
|
||||||
getTopReply replyP = do
|
getTopReply replyP = do
|
||||||
((_result, widget), enctype) <- runFormPost newMessageForm
|
((_result, widget), enctype) <- runFormPost newMessageForm
|
||||||
|
@ -305,3 +225,4 @@ postReply hDest recipsA recipsC context recipF replyG replyP after getdid midPar
|
||||||
case mlmid of
|
case mlmid of
|
||||||
Nothing -> error "noteC succeeded but no lmid found for obiid"
|
Nothing -> error "noteC succeeded but no lmid found for obiid"
|
||||||
Just lmid -> redirect $ after lmid
|
Just lmid -> redirect $ after lmid
|
||||||
|
-}
|
||||||
|
|
|
@ -14,8 +14,7 @@
|
||||||
-}
|
-}
|
||||||
|
|
||||||
module Vervis.Handler.Git
|
module Vervis.Handler.Git
|
||||||
( getGitRefDiscoverR
|
(
|
||||||
, postGitUploadRequestR
|
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
|
@ -50,42 +49,6 @@ import Vervis.Foundation (Handler)
|
||||||
import Vervis.Model.Ident
|
import Vervis.Model.Ident
|
||||||
import Vervis.Path (askRepoDir)
|
import Vervis.Path (askRepoDir)
|
||||||
|
|
||||||
getGitRefDiscoverR :: ShrIdent -> RpIdent -> Handler TypedContent
|
|
||||||
getGitRefDiscoverR shr rp = do
|
|
||||||
let typ = "application/x-git-upload-pack-advertisement"
|
|
||||||
path <- askRepoDir shr rp
|
|
||||||
let pathG = fromString path
|
|
||||||
seemsThere <- liftIO $ isRepo pathG
|
|
||||||
if seemsThere
|
|
||||||
then do
|
|
||||||
rq <- getRequest
|
|
||||||
case reqGetParams rq of
|
|
||||||
[("service", serv)] ->
|
|
||||||
if serv == "git-upload-pack"
|
|
||||||
then do
|
|
||||||
let settings =
|
|
||||||
( proc "git"
|
|
||||||
[ "upload-pack"
|
|
||||||
, "--stateless-rpc"
|
|
||||||
, "--advertise-refs"
|
|
||||||
, path
|
|
||||||
]
|
|
||||||
)
|
|
||||||
{ std_out = CreatePipe
|
|
||||||
}
|
|
||||||
(_, mh, _, _) <-
|
|
||||||
liftIO $ createProcess settings
|
|
||||||
let h = fromJust mh
|
|
||||||
refs <- liftIO $ B.hGetContents h
|
|
||||||
let content = runPut $ do
|
|
||||||
putService UploadPack
|
|
||||||
putByteString refs
|
|
||||||
setHeader "Cache-Control" "no-cache"
|
|
||||||
return $ TypedContent typ $ toContent content
|
|
||||||
else permissionDenied "Service not supported"
|
|
||||||
_ -> notFound
|
|
||||||
else notFound
|
|
||||||
|
|
||||||
{-
|
{-
|
||||||
getGitRefDiscoverR :: ShrIdent -> RpIdent -> Handler GitRefDiscovery
|
getGitRefDiscoverR :: ShrIdent -> RpIdent -> Handler GitRefDiscovery
|
||||||
getGitRefDiscoverR shar repo = do
|
getGitRefDiscoverR shar repo = do
|
||||||
|
@ -108,39 +71,6 @@ getGitRefDiscoverR shar repo = do
|
||||||
else notFound
|
else notFound
|
||||||
-}
|
-}
|
||||||
|
|
||||||
postGitUploadRequestR :: ShrIdent -> RpIdent -> Handler TypedContent
|
|
||||||
postGitUploadRequestR shr rp = do
|
|
||||||
let typ = "application/x-git-upload-pack-result"
|
|
||||||
path <- askRepoDir shr rp
|
|
||||||
let pathG = fromString path
|
|
||||||
seemsThere <- liftIO $ isRepo pathG
|
|
||||||
if seemsThere
|
|
||||||
then do
|
|
||||||
getBody <- strictRequestBody <$> waiRequest
|
|
||||||
body <- liftIO getBody
|
|
||||||
let settings =
|
|
||||||
( proc "git"
|
|
||||||
[ "upload-pack"
|
|
||||||
, "--stateless-rpc"
|
|
||||||
, path
|
|
||||||
]
|
|
||||||
)
|
|
||||||
{ std_in = CreatePipe
|
|
||||||
, std_out = CreatePipe
|
|
||||||
}
|
|
||||||
(mhin, mhout, _, _) <- liftIO $ createProcess settings
|
|
||||||
let hin = fromJust mhin
|
|
||||||
hout = fromJust mhout
|
|
||||||
liftIO $ BL.hPut hin body >> hClose hin
|
|
||||||
setHeader "Cache-Control" "no-cache"
|
|
||||||
let loop = do
|
|
||||||
b <- liftIO $ B.hGet hout BLI.defaultChunkSize
|
|
||||||
unless (B.null b) $ do
|
|
||||||
sendChunkBS b
|
|
||||||
loop
|
|
||||||
respondSource typ loop
|
|
||||||
else notFound
|
|
||||||
|
|
||||||
{- This is commented out for now because it doesn't work. The 'collectObjIds'
|
{- This is commented out for now because it doesn't work. The 'collectObjIds'
|
||||||
- function file descriptor exhaustion. I don't know whether and how I can fix
|
- function file descriptor exhaustion. I don't know whether and how I can fix
|
||||||
- that. Maybe dive deep into what happens under the hood in 'hit', or make a
|
- that. Maybe dive deep into what happens under the hood in 'hit', or make a
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{- This file is part of Vervis.
|
{- This file is part of Vervis.
|
||||||
-
|
-
|
||||||
- Written in 2016, 2019 by fr33domlover <fr33domlover@riseup.net>.
|
- Written in 2016, 2019, 2022 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.
|
||||||
-
|
-
|
||||||
|
@ -14,16 +14,32 @@
|
||||||
-}
|
-}
|
||||||
|
|
||||||
module Vervis.Handler.Group
|
module Vervis.Handler.Group
|
||||||
( getGroupsR
|
( getGroupR
|
||||||
|
, getGroupInboxR
|
||||||
|
, postGroupInboxR
|
||||||
|
, getGroupOutboxR
|
||||||
|
, getGroupOutboxItemR
|
||||||
|
, getGroupFollowersR
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{-
|
||||||
|
, getGroupsR
|
||||||
, postGroupsR
|
, postGroupsR
|
||||||
, getGroupNewR
|
, getGroupNewR
|
||||||
, getGroup
|
|
||||||
, getGroupMembersR
|
, getGroupMembersR
|
||||||
, postGroupMembersR
|
, postGroupMembersR
|
||||||
, getGroupMemberNewR
|
, getGroupMemberNewR
|
||||||
, getGroupMemberR
|
, getGroupMemberR
|
||||||
, deleteGroupMemberR
|
, deleteGroupMemberR
|
||||||
, postGroupMemberR
|
, postGroupMemberR
|
||||||
|
-}
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
|
@ -39,19 +55,95 @@ import Yesod.Core.Content (TypedContent)
|
||||||
import Yesod.Core.Handler
|
import Yesod.Core.Handler
|
||||||
import Yesod.Form.Functions (runFormPost)
|
import Yesod.Form.Functions (runFormPost)
|
||||||
import Yesod.Form.Types (FormResult (..))
|
import Yesod.Form.Types (FormResult (..))
|
||||||
import Yesod.Persist.Core (runDB, getBy404)
|
import Yesod.Persist.Core
|
||||||
|
|
||||||
import qualified Database.Esqueleto as E
|
import qualified Database.Esqueleto as E
|
||||||
|
|
||||||
import Vervis.Form.Group
|
import Yesod.ActivityPub
|
||||||
|
import Yesod.FedURI
|
||||||
|
import Yesod.Hashids
|
||||||
|
|
||||||
|
import qualified Web.ActivityPub as AP
|
||||||
|
|
||||||
|
import Vervis.Actor
|
||||||
import Vervis.Foundation
|
import Vervis.Foundation
|
||||||
import Vervis.Model
|
import Vervis.Model
|
||||||
import Vervis.Model.Group
|
import Vervis.Model.Group
|
||||||
import Vervis.Model.Ident (ShrIdent, shr2text)
|
|
||||||
import Vervis.Settings (widgetFile)
|
import Vervis.Settings (widgetFile)
|
||||||
import Vervis.Time (showDate)
|
import Vervis.Time (showDate)
|
||||||
import Vervis.Widget.Sharer
|
|
||||||
|
|
||||||
|
getGroupR :: KeyHashid Group -> Handler TypedContent
|
||||||
|
getGroupR groupHash = do
|
||||||
|
groupID <- decodeKeyHashid404 groupHash
|
||||||
|
(group, actor) <- runDB $ do
|
||||||
|
g <- get404 groupID
|
||||||
|
(g,) <$> getJust (groupActor g)
|
||||||
|
|
||||||
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
|
|
||||||
|
let route mk = encodeRouteLocal $ mk groupHash
|
||||||
|
groupAP = AP.Actor
|
||||||
|
{ AP.actorLocal = AP.ActorLocal
|
||||||
|
{ AP.actorId = route GroupR
|
||||||
|
, AP.actorInbox = route GroupInboxR
|
||||||
|
, AP.actorOutbox = Just $ route GroupOutboxR
|
||||||
|
, AP.actorFollowers = Just $ route GroupFollowersR
|
||||||
|
, AP.actorFollowing = Nothing
|
||||||
|
, AP.actorPublicKeys =
|
||||||
|
[ Left $ encodeRouteLocal ActorKey1R
|
||||||
|
, Left $ encodeRouteLocal ActorKey2R
|
||||||
|
]
|
||||||
|
, AP.actorSshKeys = []
|
||||||
|
}
|
||||||
|
, AP.actorDetail = AP.ActorDetail
|
||||||
|
{ AP.actorType = AP.ActorTypeOther "Group"
|
||||||
|
, AP.actorUsername = Nothing
|
||||||
|
, AP.actorName = Just $ actorName actor
|
||||||
|
, AP.actorSummary = Just $ actorDesc actor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
provideHtmlAndAP groupAP $ redirectToPrettyJSON here
|
||||||
|
where
|
||||||
|
here = GroupR groupHash
|
||||||
|
|
||||||
|
getGroupInboxR :: KeyHashid Group -> Handler TypedContent
|
||||||
|
getGroupInboxR = getInbox GroupInboxR groupActor
|
||||||
|
|
||||||
|
postGroupInboxR :: KeyHashid Group -> Handler TypedContent
|
||||||
|
postGroupInboxR _ = error "Temporarily disabled"
|
||||||
|
|
||||||
|
getGroupOutboxR :: KeyHashid Group -> Handler TypedContent
|
||||||
|
getGroupOutboxR = getOutbox GroupOutboxR groupActor
|
||||||
|
|
||||||
|
getGroupOutboxItemR
|
||||||
|
:: KeyHashid Group -> KeyHashid OutboxItem -> Handler TypedContent
|
||||||
|
getGroupOutboxItemR = getOutboxItem GroupOutboxItemR groupActor
|
||||||
|
|
||||||
|
getGroupFollowersR :: KeyHashid Group -> Handler TypedContent
|
||||||
|
getGroupFollowersR = getActorFollowersCollection GroupFollowersR groupActor
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{-
|
||||||
getGroupsR :: Handler Html
|
getGroupsR :: Handler Html
|
||||||
getGroupsR = do
|
getGroupsR = do
|
||||||
groups <- runDB $ select $ from $ \ (sharer, group) -> do
|
groups <- runDB $ select $ from $ \ (sharer, group) -> do
|
||||||
|
@ -98,10 +190,6 @@ getGroupNewR = do
|
||||||
((_result, widget), enctype) <- runFormPost newGroupForm
|
((_result, widget), enctype) <- runFormPost newGroupForm
|
||||||
defaultLayout $(widgetFile "group/new")
|
defaultLayout $(widgetFile "group/new")
|
||||||
|
|
||||||
getGroup :: ShrIdent -> Group -> Handler TypedContent
|
|
||||||
getGroup shar group = selectRep $ provideRep $
|
|
||||||
defaultLayout $(widgetFile "group/one")
|
|
||||||
|
|
||||||
getGroupMembersR :: ShrIdent -> Handler Html
|
getGroupMembersR :: ShrIdent -> Handler Html
|
||||||
getGroupMembersR shar = do
|
getGroupMembersR shar = do
|
||||||
(group, members) <- runDB $ do
|
(group, members) <- runDB $ do
|
||||||
|
@ -211,3 +299,4 @@ postGroupMemberR grp memb = do
|
||||||
case mmethod of
|
case mmethod of
|
||||||
Just "DELETE" -> deleteGroupMemberR grp memb
|
Just "DELETE" -> deleteGroupMemberR grp memb
|
||||||
_ -> notFound
|
_ -> notFound
|
||||||
|
-}
|
||||||
|
|
|
@ -1,70 +0,0 @@
|
||||||
{- This file is part of Vervis.
|
|
||||||
-
|
|
||||||
- Written in 2016, 2019 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.Home
|
|
||||||
( getHomeR
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Database.Esqueleto hiding ((==.))
|
|
||||||
import Yesod.Auth.Account (newAccountR)
|
|
||||||
import Data.Time.Clock
|
|
||||||
import Data.Time.Clock.POSIX (posixSecondsToUTCTime)
|
|
||||||
import Data.Traversable
|
|
||||||
import Database.Persist
|
|
||||||
import Time.Types (Elapsed (..), Seconds (..))
|
|
||||||
import Yesod.Auth
|
|
||||||
import Yesod.Core
|
|
||||||
import Yesod.Persist.Core
|
|
||||||
|
|
||||||
import qualified Database.Esqueleto as E ((==.))
|
|
||||||
|
|
||||||
import Data.EventTime.Local
|
|
||||||
|
|
||||||
import Vervis.Darcs
|
|
||||||
import Vervis.Foundation
|
|
||||||
import Vervis.Model
|
|
||||||
import Vervis.Model.Ident
|
|
||||||
import Development.PatchMediaType
|
|
||||||
import Vervis.Path
|
|
||||||
import Vervis.Settings
|
|
||||||
|
|
||||||
import qualified Vervis.Git as G
|
|
||||||
import qualified Vervis.Darcs as D
|
|
||||||
|
|
||||||
personalOverview :: Entity Person -> Handler Html
|
|
||||||
personalOverview (Entity _pid person) = do
|
|
||||||
(ident, projects, repos) <- runDB $ do
|
|
||||||
let sid = personIdent person
|
|
||||||
sharer <- get404 sid
|
|
||||||
prjs <-
|
|
||||||
map (projectIdent . entityVal) <$>
|
|
||||||
selectList [ProjectSharer ==. sid] [Asc ProjectIdent]
|
|
||||||
rps <-
|
|
||||||
map (repoIdent . entityVal) <$>
|
|
||||||
selectList
|
|
||||||
[RepoSharer ==. sid, RepoProject ==. Nothing]
|
|
||||||
[Asc RepoIdent]
|
|
||||||
return (sharerIdent sharer, prjs, rps)
|
|
||||||
defaultLayout $ do
|
|
||||||
setTitle "Vervis > Overview"
|
|
||||||
$(widgetFile "personal-overview")
|
|
||||||
|
|
||||||
getHomeR :: Handler Html
|
|
||||||
getHomeR = do
|
|
||||||
mp <- maybeAuth
|
|
||||||
case mp of
|
|
||||||
Just p -> personalOverview p
|
|
||||||
Nothing -> redirect BrowseR
|
|
|
@ -14,21 +14,22 @@
|
||||||
-}
|
-}
|
||||||
|
|
||||||
module Vervis.Handler.Inbox
|
module Vervis.Handler.Inbox
|
||||||
( getInboxDebugR
|
( getSharerInboxR
|
||||||
, getSharerInboxR
|
|
||||||
, getProjectInboxR
|
, getProjectInboxR
|
||||||
|
, getDeckInboxR
|
||||||
, getRepoInboxR
|
, getRepoInboxR
|
||||||
, postSharerInboxR
|
, postSharerInboxR
|
||||||
, postProjectInboxR
|
, postProjectInboxR
|
||||||
|
, postDeckInboxR
|
||||||
, postRepoInboxR
|
, postRepoInboxR
|
||||||
, getSharerOutboxR
|
, getSharerOutboxR
|
||||||
, getSharerOutboxItemR
|
, getSharerOutboxItemR
|
||||||
, getProjectOutboxR
|
, getProjectOutboxR
|
||||||
, getProjectOutboxItemR
|
, getProjectOutboxItemR
|
||||||
|
, getDeckOutboxR
|
||||||
|
, getDeckOutboxItemR
|
||||||
, getRepoOutboxR
|
, getRepoOutboxR
|
||||||
, getRepoOutboxItemR
|
, getRepoOutboxItemR
|
||||||
, getActorKey1R
|
|
||||||
, getActorKey2R
|
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
|
@ -103,8 +104,6 @@ import Vervis.Model.Ident
|
||||||
import Vervis.Paginate
|
import Vervis.Paginate
|
||||||
import Vervis.Settings
|
import Vervis.Settings
|
||||||
|
|
||||||
import qualified Vervis.Client as C
|
|
||||||
|
|
||||||
getShowTime = showTime <$> liftIO getCurrentTime
|
getShowTime = showTime <$> liftIO getCurrentTime
|
||||||
where
|
where
|
||||||
showTime now =
|
showTime now =
|
||||||
|
@ -123,124 +122,6 @@ objectId o =
|
||||||
Just (String t) | not (T.null t) -> t
|
Just (String t) | not (T.null t) -> t
|
||||||
_ -> error "'id' field not found"
|
_ -> error "'id' field not found"
|
||||||
|
|
||||||
getInboxDebugR :: Handler Html
|
|
||||||
getInboxDebugR = do
|
|
||||||
acts <-
|
|
||||||
liftIO . readTVarIO . snd =<< maybe notFound return =<< getsYesod appActivities
|
|
||||||
defaultLayout
|
|
||||||
[whamlet|
|
|
||||||
<p>
|
|
||||||
Welcome to the ActivityPub inbox test page! Activities received
|
|
||||||
by this Vervis instance are listed here for testing and
|
|
||||||
debugging. To test, go to another Vervis instance and publish
|
|
||||||
something that supports federation (currently, only ticket
|
|
||||||
comments), either through the regular UI or via the /publish
|
|
||||||
page, and then come back here to see the result. Activities that
|
|
||||||
aren't understood or their processing fails get listed here too,
|
|
||||||
with a report of what exactly happened.
|
|
||||||
<p>Last 10 activities posted:
|
|
||||||
<ul>
|
|
||||||
$forall ActivityReport time msg ctypes body <- acts
|
|
||||||
<li>
|
|
||||||
<div>#{show time}
|
|
||||||
<div>#{msg}
|
|
||||||
<div><code>#{intercalate " | " $ map BC.unpack ctypes}
|
|
||||||
<div><pre>#{decodeUtf8 body}
|
|
||||||
|]
|
|
||||||
|
|
||||||
getInbox :: Route App -> AppDB InboxId -> Handler TypedContent
|
|
||||||
getInbox here getInboxId = do
|
|
||||||
(total, pages, mpage) <- runDB $ do
|
|
||||||
ibid <- getInboxId
|
|
||||||
getPageAndNavCount
|
|
||||||
(countItems ibid)
|
|
||||||
(\ off lim -> map adaptItem <$> getItems ibid off lim)
|
|
||||||
encodeRouteLocal <- getEncodeRouteLocal
|
|
||||||
encodeRoutePageLocal <- getEncodeRoutePageLocal
|
|
||||||
let pageUrl = encodeRoutePageLocal here
|
|
||||||
host <- getsYesod $ appInstanceHost . appSettings
|
|
||||||
selectRep $
|
|
||||||
case mpage of
|
|
||||||
Nothing -> do
|
|
||||||
provideAP $ pure $ Doc host $ Collection
|
|
||||||
{ collectionId = encodeRouteLocal here
|
|
||||||
, collectionType = CollectionTypeOrdered
|
|
||||||
, collectionTotalItems = Just total
|
|
||||||
, collectionCurrent = Nothing
|
|
||||||
, collectionFirst = Just $ pageUrl 1
|
|
||||||
, collectionLast = Just $ pageUrl pages
|
|
||||||
, collectionItems = [] :: [Text]
|
|
||||||
}
|
|
||||||
provideRep (redirectFirstPage here :: Handler Html)
|
|
||||||
Just (items, navModel) -> do
|
|
||||||
let current = nmCurrent navModel
|
|
||||||
provideAP $ pure $ Doc host $ 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 fst items
|
|
||||||
}
|
|
||||||
provideRep $ do
|
|
||||||
let pageNav = navWidget navModel
|
|
||||||
showTime <- getShowTime
|
|
||||||
defaultLayout $(widgetFile "person/inbox")
|
|
||||||
where
|
|
||||||
countItems ibid =
|
|
||||||
(+) <$> count [InboxItemLocalInbox ==. ibid]
|
|
||||||
<*> count [InboxItemRemoteInbox ==. ibid]
|
|
||||||
getItems ibid off lim =
|
|
||||||
E.select $ E.from $
|
|
||||||
\ (ib `E.LeftOuterJoin` (ibl `E.InnerJoin` ob) `E.LeftOuterJoin` (ibr `E.InnerJoin` ract)) -> do
|
|
||||||
E.on $ ibr E.?. InboxItemRemoteActivity E.==. ract E.?. RemoteActivityId
|
|
||||||
E.on $ E.just (ib E.^. InboxItemId) E.==. ibr E.?. InboxItemRemoteItem
|
|
||||||
E.on $ ibl E.?. InboxItemLocalActivity E.==. ob E.?. OutboxItemId
|
|
||||||
E.on $ E.just (ib E.^. InboxItemId) E.==. ibl E.?. InboxItemLocalItem
|
|
||||||
E.where_
|
|
||||||
$ ( E.isNothing (ibr E.?. InboxItemRemoteInbox) E.||.
|
|
||||||
ibr E.?. InboxItemRemoteInbox E.==. E.just (E.val ibid)
|
|
||||||
)
|
|
||||||
E.&&.
|
|
||||||
( E.isNothing (ibl E.?. InboxItemLocalInbox) E.||.
|
|
||||||
ibl E.?. InboxItemLocalInbox E.==. E.just (E.val ibid)
|
|
||||||
)
|
|
||||||
E.orderBy [E.desc $ ib E.^. InboxItemId]
|
|
||||||
E.offset $ fromIntegral off
|
|
||||||
E.limit $ fromIntegral lim
|
|
||||||
return
|
|
||||||
( ib E.^. InboxItemId
|
|
||||||
, ob E.?. OutboxItemActivity
|
|
||||||
, ob E.?. OutboxItemPublished
|
|
||||||
, ract E.?. RemoteActivityContent
|
|
||||||
, ract E.?. RemoteActivityReceived
|
|
||||||
)
|
|
||||||
adaptItem
|
|
||||||
(E.Value ibid, E.Value mact, E.Value mpub, E.Value mobj, E.Value mrec) =
|
|
||||||
case (mact, mpub, mobj, mrec) of
|
|
||||||
(Nothing, Nothing, Nothing, Nothing) ->
|
|
||||||
error $ ibiidString ++ " neither local nor remote"
|
|
||||||
(Just _, Just _, Just _, Just _) ->
|
|
||||||
error $ ibiidString ++ " both local and remote"
|
|
||||||
(Just act, Just pub, Nothing, Nothing) ->
|
|
||||||
(persistJSONObject act, (pub, False))
|
|
||||||
(Nothing, Nothing, Just obj, Just rec) ->
|
|
||||||
(persistJSONObject obj, (rec, True))
|
|
||||||
_ -> error $ "Unexpected query result for " ++ ibiidString
|
|
||||||
where
|
|
||||||
ibiidString = "InboxItem #" ++ show (fromSqlKey ibid)
|
|
||||||
|
|
||||||
getSharerInboxR :: ShrIdent -> Handler TypedContent
|
getSharerInboxR :: ShrIdent -> Handler TypedContent
|
||||||
getSharerInboxR shr = getInbox here getInboxId
|
getSharerInboxR shr = getInbox here getInboxId
|
||||||
where
|
where
|
||||||
|
@ -260,6 +141,16 @@ getProjectInboxR shr prj = getInbox here getInboxId
|
||||||
a <- getJust $ projectActor j
|
a <- getJust $ projectActor j
|
||||||
return $ actorInbox a
|
return $ actorInbox a
|
||||||
|
|
||||||
|
getDeckInboxR :: KeyHashid Project -> Handler TypedContent
|
||||||
|
getDeckInboxR dkkhid = do
|
||||||
|
dkid <- decodeKeyHashid404 dkkhid
|
||||||
|
getInbox here (getInboxId dkid)
|
||||||
|
where
|
||||||
|
here = ProjectInboxR dkkhid
|
||||||
|
getInboxId dkid = do
|
||||||
|
dk <- get404 dkid
|
||||||
|
actorInbox <$> getJust (projectActor dk)
|
||||||
|
|
||||||
getRepoInboxR :: ShrIdent -> RpIdent -> Handler TypedContent
|
getRepoInboxR :: ShrIdent -> RpIdent -> Handler TypedContent
|
||||||
getRepoInboxR shr rp = getInbox here getInboxId
|
getRepoInboxR shr rp = getInbox here getInboxId
|
||||||
where
|
where
|
||||||
|
@ -330,6 +221,9 @@ postSharerInboxR shrRecip = handleInbox $ handleSharerInbox shrRecip
|
||||||
postProjectInboxR :: ShrIdent -> PrjIdent -> Handler ()
|
postProjectInboxR :: ShrIdent -> PrjIdent -> Handler ()
|
||||||
postProjectInboxR shr prj = handleInbox $ handleProjectInbox shr prj
|
postProjectInboxR shr prj = handleInbox $ handleProjectInbox shr prj
|
||||||
|
|
||||||
|
postDeckInboxR :: KeyHashid Project -> Handler ()
|
||||||
|
postDeckInboxR dkkhid = handleInbox $ handleDeckInbox dkkhid
|
||||||
|
|
||||||
postRepoInboxR :: ShrIdent -> RpIdent -> Handler ()
|
postRepoInboxR :: ShrIdent -> RpIdent -> Handler ()
|
||||||
postRepoInboxR shr rp = handleInbox $ handleRepoInbox shr rp
|
postRepoInboxR shr rp = handleInbox $ handleRepoInbox shr rp
|
||||||
|
|
||||||
|
@ -342,70 +236,6 @@ jsonField = checkMMap fromTextarea toTextarea textareaField
|
||||||
-}
|
-}
|
||||||
|
|
||||||
|
|
||||||
getOutbox :: Route App -> AppDB OutboxId -> Handler TypedContent
|
|
||||||
getOutbox here getObid = do
|
|
||||||
(total, pages, mpage) <- runDB $ do
|
|
||||||
obid <- getObid
|
|
||||||
let countAllItems = count [OutboxItemOutbox ==. obid]
|
|
||||||
selectItems off lim = selectList [OutboxItemOutbox ==. obid] [Desc OutboxItemId, OffsetBy off, LimitTo lim]
|
|
||||||
getPageAndNavCount countAllItems selectItems
|
|
||||||
encodeRouteLocal <- getEncodeRouteLocal
|
|
||||||
encodeRoutePageLocal <- getEncodeRoutePageLocal
|
|
||||||
let pageUrl = encodeRoutePageLocal here
|
|
||||||
host <- getsYesod $ appInstanceHost . appSettings
|
|
||||||
selectRep $
|
|
||||||
case mpage of
|
|
||||||
Nothing -> do
|
|
||||||
provideAP $ pure $ Doc host $ Collection
|
|
||||||
{ collectionId = encodeRouteLocal here
|
|
||||||
, collectionType = CollectionTypeOrdered
|
|
||||||
, collectionTotalItems = Just total
|
|
||||||
, collectionCurrent = Nothing
|
|
||||||
, collectionFirst = Just $ pageUrl 1
|
|
||||||
, collectionLast = Just $ pageUrl pages
|
|
||||||
, collectionItems = [] :: [Text]
|
|
||||||
}
|
|
||||||
provideRep (redirectFirstPage here :: Handler Html)
|
|
||||||
Just (items, navModel) -> do
|
|
||||||
let current = nmCurrent navModel
|
|
||||||
provideAP $ pure $ Doc host $ 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 (persistJSONObject . outboxItemActivity . entityVal) items
|
|
||||||
}
|
|
||||||
provideRep $ do
|
|
||||||
let pageNav = navWidget navModel
|
|
||||||
showTime <- getShowTime
|
|
||||||
defaultLayout $(widgetFile "person/outbox")
|
|
||||||
|
|
||||||
getOutboxItem
|
|
||||||
:: Route App
|
|
||||||
-> AppDB OutboxId
|
|
||||||
-> KeyHashid OutboxItem
|
|
||||||
-> Handler TypedContent
|
|
||||||
getOutboxItem here getObid obikhid = do
|
|
||||||
obiid <- decodeKeyHashid404 obikhid
|
|
||||||
body <- runDB $ do
|
|
||||||
obid <- getObid
|
|
||||||
obi <- get404 obiid
|
|
||||||
unless (outboxItemOutbox obi == obid) notFound
|
|
||||||
return $ outboxItemActivity obi
|
|
||||||
provideHtmlAndAP'' body $ redirect (here, [("prettyjson", "true")])
|
|
||||||
|
|
||||||
getSharerOutboxR :: ShrIdent -> Handler TypedContent
|
getSharerOutboxR :: ShrIdent -> Handler TypedContent
|
||||||
getSharerOutboxR shr = getOutbox here getObid
|
getSharerOutboxR shr = getOutbox here getObid
|
||||||
where
|
where
|
||||||
|
@ -445,6 +275,27 @@ getProjectOutboxItemR shr prj obikhid = getOutboxItem here getObid obikhid
|
||||||
a <- getJust $ projectActor j
|
a <- getJust $ projectActor j
|
||||||
return $ actorOutbox a
|
return $ actorOutbox a
|
||||||
|
|
||||||
|
getDeckOutboxR :: KeyHashid Project -> Handler TypedContent
|
||||||
|
getDeckOutboxR dkkhid = do
|
||||||
|
dkid <- decodeKeyHashid404 dkkhid
|
||||||
|
getOutbox here (getObid dkid)
|
||||||
|
where
|
||||||
|
here = DeckOutboxR dkkhid
|
||||||
|
getObid dkid = do
|
||||||
|
dk <- get404 dkid
|
||||||
|
actorOutbox <$> getJust (projectActor dk)
|
||||||
|
|
||||||
|
getDeckOutboxItemR
|
||||||
|
:: KeyHashid Project -> KeyHashid OutboxItem -> Handler TypedContent
|
||||||
|
getDeckOutboxItemR dkkhid obikhid = do
|
||||||
|
dkid <- decodeKeyHashid404 dkkhid
|
||||||
|
getOutboxItem here (getObid dkid) obikhid
|
||||||
|
where
|
||||||
|
here = DeckOutboxItemR dkkhid obikhid
|
||||||
|
getObid dkid = do
|
||||||
|
dk <- get404 dkid
|
||||||
|
actorOutbox <$> getJust (projectActor dk)
|
||||||
|
|
||||||
getRepoOutboxR :: ShrIdent -> RpIdent -> Handler TypedContent
|
getRepoOutboxR :: ShrIdent -> RpIdent -> Handler TypedContent
|
||||||
getRepoOutboxR shr rp = getOutbox here getObid
|
getRepoOutboxR shr rp = getOutbox here getObid
|
||||||
where
|
where
|
||||||
|
@ -463,23 +314,3 @@ getRepoOutboxItemR shr rp obikhid = getOutboxItem here getObid obikhid
|
||||||
sid <- getKeyBy404 $ UniqueSharer shr
|
sid <- getKeyBy404 $ UniqueSharer shr
|
||||||
r <- getValBy404 $ UniqueRepo rp sid
|
r <- getValBy404 $ UniqueRepo rp sid
|
||||||
return $ repoOutbox r
|
return $ repoOutbox r
|
||||||
|
|
||||||
getActorKey :: ((ActorKey, ActorKey, Bool) -> ActorKey) -> Route App -> Handler TypedContent
|
|
||||||
getActorKey choose route = do
|
|
||||||
actorKey <-
|
|
||||||
liftIO . fmap (actorKeyPublicBin . choose) . readTVarIO =<<
|
|
||||||
getsYesod appActorKeys
|
|
||||||
encodeRouteLocal <- getEncodeRouteLocal
|
|
||||||
let key = PublicKey
|
|
||||||
{ publicKeyId = LocalRefURI $ Left $ encodeRouteLocal route
|
|
||||||
, publicKeyExpires = Nothing
|
|
||||||
, publicKeyOwner = OwnerInstance
|
|
||||||
, publicKeyMaterial = actorKey
|
|
||||||
}
|
|
||||||
provideHtmlAndAP key $ redirect (route, [("prettyjson", "true")])
|
|
||||||
|
|
||||||
getActorKey1R :: Handler TypedContent
|
|
||||||
getActorKey1R = getActorKey (\ (k1, _, _) -> k1) ActorKey1R
|
|
||||||
|
|
||||||
getActorKey2R :: Handler TypedContent
|
|
||||||
getActorKey2R = getActorKey (\ (_, k2, _) -> k2) ActorKey2R
|
|
||||||
|
|
|
@ -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, 2022 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.
|
||||||
-
|
-
|
||||||
|
@ -18,7 +18,6 @@ module Vervis.Handler.Key
|
||||||
, postKeysR
|
, postKeysR
|
||||||
, getKeyNewR
|
, getKeyNewR
|
||||||
, getKeyR
|
, getKeyR
|
||||||
, getSshKeyR
|
|
||||||
, deleteKeyR
|
, deleteKeyR
|
||||||
, postKeyR
|
, postKeyR
|
||||||
)
|
)
|
||||||
|
@ -55,6 +54,7 @@ import Vervis.Model.Ident
|
||||||
import Vervis.Settings
|
import Vervis.Settings
|
||||||
import Vervis.Widget (buttonW)
|
import Vervis.Widget (buttonW)
|
||||||
|
|
||||||
|
{-
|
||||||
getKeysR :: Handler Html
|
getKeysR :: Handler Html
|
||||||
getKeysR = do
|
getKeysR = do
|
||||||
pid <- requireAuthId
|
pid <- requireAuthId
|
||||||
|
@ -92,30 +92,9 @@ getKeyR tag = do
|
||||||
let toText = decodeUtf8With lenientDecode
|
let toText = decodeUtf8With lenientDecode
|
||||||
content = toText $ encode $ sshKeyContent key
|
content = toText $ encode $ sshKeyContent key
|
||||||
defaultLayout $(widgetFile "key/one")
|
defaultLayout $(widgetFile "key/one")
|
||||||
|
-}
|
||||||
|
|
||||||
getSshKeyR :: ShrIdent -> KeyHashid SshKey -> Handler TypedContent
|
{-
|
||||||
getSshKeyR shr skkhid = do
|
|
||||||
skid <- decodeKeyHashid404 skkhid
|
|
||||||
key <- runDB $ do
|
|
||||||
sid <- getKeyBy404 $ UniqueSharer shr
|
|
||||||
pid <- getKeyBy404 $ UniquePersonIdent sid
|
|
||||||
sk <- get404 skid
|
|
||||||
unless (sshKeyPerson sk == pid) notFound
|
|
||||||
return sk
|
|
||||||
encodeRouteLocal <- getEncodeRouteLocal
|
|
||||||
let here = SshKeyR shr skkhid
|
|
||||||
keyAP = SshPublicKey
|
|
||||||
{ sshPublicKeyId = encodeRouteLocal here
|
|
||||||
, sshPublicKeyExpires = Nothing
|
|
||||||
, sshPublicKeyOwner = encodeRouteLocal $ SharerR shr
|
|
||||||
, sshPublicKeyAlgorithm =
|
|
||||||
case sshKeyAlgo key of
|
|
||||||
"ssh-rsa" -> SshKeyAlgorithmRSA
|
|
||||||
_ -> error "Unexpected sshKeyAlgo in DB"
|
|
||||||
, sshPublicKeyMaterial = sshKeyContent key
|
|
||||||
}
|
|
||||||
provideHtmlAndAP keyAP $ redirectToPrettyJSON here
|
|
||||||
|
|
||||||
deleteKeyR :: KyIdent -> Handler Html
|
deleteKeyR :: KyIdent -> Handler Html
|
||||||
deleteKeyR tag = do
|
deleteKeyR tag = do
|
||||||
pid <- requireAuthId
|
pid <- requireAuthId
|
||||||
|
@ -131,3 +110,4 @@ postKeyR tag = do
|
||||||
case mmethod of
|
case mmethod of
|
||||||
Just "DELETE" -> deleteKeyR tag
|
Just "DELETE" -> deleteKeyR tag
|
||||||
_ -> notFound
|
_ -> notFound
|
||||||
|
-}
|
||||||
|
|
200
src/Vervis/Handler/Loom.hs
Normal file
200
src/Vervis/Handler/Loom.hs
Normal file
|
@ -0,0 +1,200 @@
|
||||||
|
{- This file is part of Vervis.
|
||||||
|
-
|
||||||
|
- Written in 2022 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.Loom
|
||||||
|
( getLoomR
|
||||||
|
, getLoomInboxR
|
||||||
|
, postLoomInboxR
|
||||||
|
, getLoomOutboxR
|
||||||
|
, getLoomOutboxItemR
|
||||||
|
, getLoomFollowersR
|
||||||
|
, getLoomClothsR
|
||||||
|
)
|
||||||
|
where
|
||||||
|
|
||||||
|
import Control.Monad
|
||||||
|
import Control.Monad.Trans.Except
|
||||||
|
import Data.Aeson
|
||||||
|
import Data.Foldable
|
||||||
|
import Data.Maybe (fromMaybe)
|
||||||
|
import Data.Text (Text)
|
||||||
|
import Data.Time.Clock
|
||||||
|
import Data.Traversable
|
||||||
|
import Database.Persist
|
||||||
|
import Text.Blaze.Html (Html)
|
||||||
|
import Yesod.Auth (requireAuth)
|
||||||
|
import Yesod.Core
|
||||||
|
import Yesod.Core.Handler (redirect, setMessage, lookupPostParam, notFound)
|
||||||
|
import Yesod.Form.Functions (runFormPost)
|
||||||
|
import Yesod.Form.Types (FormResult (..))
|
||||||
|
import Yesod.Persist.Core (runDB, get404, getBy404)
|
||||||
|
|
||||||
|
import qualified Data.ByteString.Lazy as BL
|
||||||
|
import qualified Database.Esqueleto as E
|
||||||
|
|
||||||
|
import Database.Persist.JSON
|
||||||
|
import Development.PatchMediaType
|
||||||
|
import Network.FedURI
|
||||||
|
import Yesod.ActivityPub
|
||||||
|
import Yesod.Hashids
|
||||||
|
import Yesod.FedURI
|
||||||
|
import Yesod.MonadSite
|
||||||
|
|
||||||
|
import qualified Web.ActivityPub as AP
|
||||||
|
|
||||||
|
import Control.Monad.Trans.Except.Local
|
||||||
|
import Data.Either.Local
|
||||||
|
import Data.Paginate.Local
|
||||||
|
import Database.Persist.Local
|
||||||
|
import Yesod.Persist.Local
|
||||||
|
|
||||||
|
import Vervis.Actor
|
||||||
|
import Vervis.API
|
||||||
|
import Vervis.Federation
|
||||||
|
import Vervis.Foundation
|
||||||
|
import Vervis.Model
|
||||||
|
import Vervis.Paginate
|
||||||
|
import Vervis.Settings
|
||||||
|
|
||||||
|
getLoomR :: KeyHashid Loom -> Handler TypedContent
|
||||||
|
getLoomR loomHash = do
|
||||||
|
loomID <- decodeKeyHashid404 loomHash
|
||||||
|
(loom, actor) <- runDB $ do
|
||||||
|
l <- get404 loomID
|
||||||
|
(l,) <$> getJust (loomActor l)
|
||||||
|
|
||||||
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
|
let route mk = encodeRouteLocal $ mk loomHash
|
||||||
|
loomAP = AP.Actor
|
||||||
|
{ AP.actorLocal = AP.ActorLocal
|
||||||
|
{ AP.actorId = route LoomR
|
||||||
|
, AP.actorInbox = route LoomInboxR
|
||||||
|
, AP.actorOutbox = Just $ route LoomOutboxR
|
||||||
|
, AP.actorFollowers = Just $ route LoomFollowersR
|
||||||
|
, AP.actorFollowing = Nothing
|
||||||
|
, AP.actorPublicKeys =
|
||||||
|
[ Left $ encodeRouteLocal ActorKey1R
|
||||||
|
, Left $ encodeRouteLocal ActorKey2R
|
||||||
|
]
|
||||||
|
, AP.actorSshKeys = []
|
||||||
|
}
|
||||||
|
, AP.actorDetail = AP.ActorDetail
|
||||||
|
{ AP.actorType = AP.ActorTypeOther "PatchTracker"
|
||||||
|
, AP.actorUsername = Nothing
|
||||||
|
, AP.actorName = Just $ actorName actor
|
||||||
|
, AP.actorSummary = Just $ actorDesc actor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
provideHtmlAndAP loomAP $ redirectToPrettyJSON here
|
||||||
|
where
|
||||||
|
here = LoomR loomHash
|
||||||
|
|
||||||
|
getLoomInboxR :: KeyHashid Loom -> Handler TypedContent
|
||||||
|
getLoomInboxR = getInbox LoomInboxR loomActor
|
||||||
|
|
||||||
|
postLoomInboxR :: KeyHashid Loom -> Handler TypedContent
|
||||||
|
postLoomInboxR _ = error "Temporarily disabled"
|
||||||
|
|
||||||
|
getLoomOutboxR :: KeyHashid Loom -> Handler TypedContent
|
||||||
|
getLoomOutboxR = getOutbox LoomOutboxR loomActor
|
||||||
|
|
||||||
|
getLoomOutboxItemR
|
||||||
|
:: KeyHashid Loom -> KeyHashid OutboxItem -> Handler TypedContent
|
||||||
|
getLoomOutboxItemR = getOutboxItem LoomOutboxItemR loomActor
|
||||||
|
|
||||||
|
getLoomFollowersR :: KeyHashid Loom -> Handler TypedContent
|
||||||
|
getLoomFollowersR = getActorFollowersCollection LoomFollowersR loomActor
|
||||||
|
|
||||||
|
getLoomClothsR :: KeyHashid Loom -> Handler TypedContent
|
||||||
|
getLoomClothsR loomHash = selectRep $ do
|
||||||
|
{-
|
||||||
|
provideRep $ do
|
||||||
|
((filtResult, filtWidget), filtEnctype) <- runFormGet ticketFilterForm
|
||||||
|
let tf =
|
||||||
|
case filtResult of
|
||||||
|
FormSuccess filt -> filt
|
||||||
|
FormMissing -> def
|
||||||
|
FormFailure l ->
|
||||||
|
error $ "Ticket filter form failed: " ++ show l
|
||||||
|
(total, pages, mpage) <- runDB $ do
|
||||||
|
Entity sid _ <- getBy404 $ UniqueSharer shr
|
||||||
|
Entity jid _ <- getBy404 $ UniqueProject prj sid
|
||||||
|
let countAllTickets = count [TicketProjectLocalProject ==. jid]
|
||||||
|
selectTickets off lim =
|
||||||
|
getTicketSummaries
|
||||||
|
(filterTickets tf)
|
||||||
|
(Just $ \ t -> [E.asc $ t E.^. TicketId])
|
||||||
|
(Just (off, lim))
|
||||||
|
jid
|
||||||
|
getPageAndNavCount countAllTickets selectTickets
|
||||||
|
case mpage of
|
||||||
|
Nothing -> redirectFirstPage here
|
||||||
|
Just (rows, navModel) ->
|
||||||
|
let pageNav = navWidget navModel
|
||||||
|
in defaultLayout $(widgetFile "ticket/list")
|
||||||
|
-}
|
||||||
|
AP.provideAP' $ do
|
||||||
|
loomID <- decodeKeyHashid404 loomHash
|
||||||
|
(total, pages, mpage) <- runDB $ do
|
||||||
|
let countAllTickets = count [TicketLoomLoom ==. loomID]
|
||||||
|
selectTickets off lim =
|
||||||
|
selectKeysList
|
||||||
|
[TicketLoomLoom ==. loomID]
|
||||||
|
[OffsetBy off, LimitTo lim, Desc TicketLoomTicket]
|
||||||
|
getPageAndNavCount countAllTickets selectTickets
|
||||||
|
|
||||||
|
encodeRouteHome <- getEncodeRouteHome
|
||||||
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
|
hashTicket <- getEncodeKeyHashid
|
||||||
|
encodeRoutePageLocal <- getEncodeRoutePageLocal
|
||||||
|
let pageUrl = encodeRoutePageLocal here
|
||||||
|
host <- asksSite siteInstanceHost
|
||||||
|
return $
|
||||||
|
case mpage of
|
||||||
|
Nothing -> encodeStrict $ AP.Doc host $ AP.Collection
|
||||||
|
{ AP.collectionId = encodeRouteLocal here
|
||||||
|
, AP.collectionType = AP.CollectionTypeOrdered
|
||||||
|
, AP.collectionTotalItems = Just total
|
||||||
|
, AP.collectionCurrent = Nothing
|
||||||
|
, AP.collectionFirst = Just $ pageUrl 1
|
||||||
|
, AP.collectionLast = Just $ pageUrl pages
|
||||||
|
, AP.collectionItems = [] :: [Text]
|
||||||
|
}
|
||||||
|
Just (tickets, navModel) ->
|
||||||
|
let current = nmCurrent navModel
|
||||||
|
in encodeStrict $ AP.Doc host $ AP.CollectionPage
|
||||||
|
{ AP.collectionPageId = pageUrl current
|
||||||
|
, AP.collectionPageType = AP.CollectionPageTypeOrdered
|
||||||
|
, AP.collectionPageTotalItems = Nothing
|
||||||
|
, AP.collectionPageCurrent = Just $ pageUrl current
|
||||||
|
, AP.collectionPageFirst = Just $ pageUrl 1
|
||||||
|
, AP.collectionPageLast = Just $ pageUrl pages
|
||||||
|
, AP.collectionPagePartOf = encodeRouteLocal here
|
||||||
|
, AP.collectionPagePrev =
|
||||||
|
if current > 1
|
||||||
|
then Just $ pageUrl $ current - 1
|
||||||
|
else Nothing
|
||||||
|
, AP.collectionPageNext =
|
||||||
|
if current < pages
|
||||||
|
then Just $ pageUrl $ current + 1
|
||||||
|
else Nothing
|
||||||
|
, AP.collectionPageStartIndex = Nothing
|
||||||
|
, AP.collectionPageItems =
|
||||||
|
encodeRouteHome . ClothR loomHash . hashTicket <$> tickets
|
||||||
|
}
|
||||||
|
where
|
||||||
|
here = LoomClothsR loomHash
|
||||||
|
encodeStrict = BL.toStrict . encode
|
|
@ -14,13 +14,28 @@
|
||||||
-}
|
-}
|
||||||
|
|
||||||
module Vervis.Handler.Person
|
module Vervis.Handler.Person
|
||||||
( getResendVerifyEmailR
|
( getPersonR
|
||||||
, getPeopleR
|
, getPersonInboxR
|
||||||
, getPerson
|
, postPersonInboxR
|
||||||
|
, getPersonOutboxR
|
||||||
|
, postPersonOutboxR
|
||||||
|
, getPersonOutboxItemR
|
||||||
|
, getPersonFollowersR
|
||||||
|
, getPersonFollowingR
|
||||||
|
, getSshKeyR
|
||||||
|
, getPersonMessageR
|
||||||
|
|
||||||
|
, postPersonFollowR
|
||||||
|
, postPersonUnfollowR
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
import Database.Esqueleto hiding (isNothing, count)
|
import Control.Monad
|
||||||
|
import Control.Monad.Trans.Except
|
||||||
|
import Data.Maybe
|
||||||
|
import Data.Traversable
|
||||||
|
import Database.Persist
|
||||||
|
import Dvara
|
||||||
import Text.Blaze.Html (toHtml)
|
import Text.Blaze.Html (toHtml)
|
||||||
import Yesod.Core
|
import Yesod.Core
|
||||||
import Yesod.Auth.Account (newAccountR, resendVerifyEmailWidget, username)
|
import Yesod.Auth.Account (newAccountR, resendVerifyEmailWidget, username)
|
||||||
|
@ -28,138 +43,254 @@ import Yesod.Auth.Account.Message (AccountMsg (MsgEmailUnverified))
|
||||||
import Yesod.Persist.Core
|
import Yesod.Persist.Core
|
||||||
|
|
||||||
import qualified Data.Text as T (unpack)
|
import qualified Data.Text as T (unpack)
|
||||||
import qualified Database.Persist as P
|
|
||||||
|
|
||||||
import Yesod.Auth.Unverified (requireUnverifiedAuth)
|
import Yesod.Auth.Unverified (requireUnverifiedAuth)
|
||||||
|
|
||||||
import Text.Email.Local
|
import Text.Email.Local
|
||||||
|
|
||||||
import Network.FedURI
|
import Network.FedURI
|
||||||
import Web.ActivityPub
|
|
||||||
import Yesod.ActivityPub
|
import Yesod.ActivityPub
|
||||||
import Yesod.FedURI
|
import Yesod.FedURI
|
||||||
import Yesod.Hashids
|
import Yesod.Hashids
|
||||||
|
|
||||||
|
import qualified Web.ActivityPub as AP
|
||||||
|
|
||||||
|
import Data.Either.Local
|
||||||
|
import Database.Persist.Local
|
||||||
|
|
||||||
|
import Vervis.ActivityPub
|
||||||
|
import Vervis.Actor
|
||||||
import Vervis.ActorKey
|
import Vervis.ActorKey
|
||||||
import Vervis.Foundation
|
import Vervis.Foundation
|
||||||
import Vervis.Model hiding (Actor (..))
|
import Vervis.Model
|
||||||
import Vervis.Model.Ident
|
import Vervis.Model.Ident
|
||||||
import Vervis.Secure
|
import Vervis.Secure
|
||||||
import Vervis.Settings
|
import Vervis.Settings
|
||||||
import Vervis.Widget (avatarW)
|
import Vervis.Ticket
|
||||||
import Vervis.Widget.Sharer
|
import Vervis.Widget
|
||||||
|
import Vervis.Widget.Person
|
||||||
|
|
||||||
-- | Account verification email resend form
|
getPersonR :: KeyHashid Person -> Handler TypedContent
|
||||||
getResendVerifyEmailR :: Handler Html
|
getPersonR personHash = do
|
||||||
getResendVerifyEmailR = do
|
personID <- decodeKeyHashid404 personHash
|
||||||
person <- requireUnverifiedAuth
|
(person, actor, sshKeyIDs) <- runDB $ do
|
||||||
defaultLayout $ do
|
p <- get404 personID
|
||||||
setTitleI MsgEmailUnverified
|
a <- getJust $ personActor p
|
||||||
[whamlet|
|
ks <- selectKeysList [SshKeyPerson ==. personID] [Asc SshKeyId]
|
||||||
<p>_{MsgEmailUnverified}
|
return (p, a, ks)
|
||||||
^{resendVerifyEmailWidget (username person) AuthR}
|
|
||||||
|]
|
|
||||||
|
|
||||||
-- | Get list of users
|
|
||||||
getPeopleR :: Handler Html
|
|
||||||
getPeopleR = do
|
|
||||||
people <- runDB $ select $ from $ \ (sharer, person) -> do
|
|
||||||
where_ $ sharer ^. SharerId ==. person ^. PersonIdent
|
|
||||||
orderBy [asc $ sharer ^. SharerIdent]
|
|
||||||
return $ sharer ^. SharerIdent
|
|
||||||
defaultLayout $(widgetFile "people")
|
|
||||||
|
|
||||||
{-
|
|
||||||
-- | Create new user
|
|
||||||
postPeopleR :: Handler Html
|
|
||||||
postPeopleR = redirect $ AuthR newAccountR
|
|
||||||
settings <- getsYesod appSettings
|
|
||||||
if appRegister settings
|
|
||||||
then do
|
|
||||||
room <- case appAccounts settings of
|
|
||||||
Nothing -> return True
|
|
||||||
Just cap -> do
|
|
||||||
current <- runDB $ count ([] :: [Filter Person])
|
|
||||||
return $ current < cap
|
|
||||||
if room
|
|
||||||
then do
|
|
||||||
((result, widget), enctype) <- runFormPost newPersonForm
|
|
||||||
case result of
|
|
||||||
FormSuccess np -> do
|
|
||||||
now <- liftIO getCurrentTime
|
|
||||||
runDB $ do
|
|
||||||
let sharer = Sharer
|
|
||||||
{ sharerIdent = npLogin np
|
|
||||||
, sharerName = npName np
|
|
||||||
, sharerCreated = now
|
|
||||||
}
|
|
||||||
sid <- insert sharer
|
|
||||||
let person = Person
|
|
||||||
{ personIdent = sid
|
|
||||||
, personLogin = shr2text $ npLogin np
|
|
||||||
, personHash = Nothing
|
|
||||||
, personEmail = npEmail np
|
|
||||||
}
|
|
||||||
person' <- setPassword (npPass np) person
|
|
||||||
insert_ person'
|
|
||||||
redirectUltDest HomeR
|
|
||||||
FormMissing -> do
|
|
||||||
setMessage "Field(s) missing"
|
|
||||||
defaultLayout $(widgetFile "person-new")
|
|
||||||
FormFailure _l -> do
|
|
||||||
setMessage
|
|
||||||
"User registration failed, see errors below"
|
|
||||||
defaultLayout $(widgetFile "person-new")
|
|
||||||
else do
|
|
||||||
setMessage "Maximal number of registered users reached"
|
|
||||||
redirect PeopleR
|
|
||||||
else do
|
|
||||||
setMessage "User registration disabled"
|
|
||||||
redirect PeopleR
|
|
||||||
-}
|
|
||||||
|
|
||||||
{-
|
|
||||||
getPersonNewR :: Handler Html
|
|
||||||
getPersonNewR = redirect $ AuthR newAccountR
|
|
||||||
regEnabled <- getsYesod $ appRegister . appSettings
|
|
||||||
if regEnabled
|
|
||||||
then do
|
|
||||||
((_result, widget), enctype) <- runFormPost newPersonForm
|
|
||||||
defaultLayout $(widgetFile "person-new")
|
|
||||||
else notFound
|
|
||||||
-}
|
|
||||||
|
|
||||||
getPerson :: ShrIdent -> Sharer -> Entity Person -> Handler TypedContent
|
|
||||||
getPerson shr sharer (Entity pid person) = do
|
|
||||||
encodeRouteLocal <- getEncodeRouteLocal
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
encodeKeyHashid <- getEncodeKeyHashid
|
hashSshKey <- getEncodeKeyHashid
|
||||||
skids <- runDB $ P.selectKeysList [SshKeyPerson P.==. pid] [P.Asc SshKeyId]
|
|
||||||
let personAP = Actor
|
let personAP = AP.Actor
|
||||||
{ actorLocal = ActorLocal
|
{ AP.actorLocal = AP.ActorLocal
|
||||||
{ actorId = encodeRouteLocal $ SharerR shr
|
{ AP.actorId = encodeRouteLocal $ PersonR personHash
|
||||||
, actorInbox = encodeRouteLocal $ SharerInboxR shr
|
, AP.actorInbox = encodeRouteLocal $ PersonInboxR personHash
|
||||||
, actorOutbox = Just $ encodeRouteLocal $ SharerOutboxR shr
|
, AP.actorOutbox = Just $ encodeRouteLocal $ PersonOutboxR personHash
|
||||||
, actorFollowers = Just $ encodeRouteLocal $ SharerFollowersR shr
|
, AP.actorFollowers = Just $ encodeRouteLocal $ PersonFollowersR personHash
|
||||||
, actorFollowing = Just $ encodeRouteLocal $ SharerFollowingR shr
|
, AP.actorFollowing = Just $ encodeRouteLocal $ PersonFollowingR personHash
|
||||||
, actorPublicKeys =
|
, AP.actorPublicKeys =
|
||||||
[ Left $ encodeRouteLocal ActorKey1R
|
[ Left $ encodeRouteLocal ActorKey1R
|
||||||
, Left $ encodeRouteLocal ActorKey2R
|
, Left $ encodeRouteLocal ActorKey2R
|
||||||
]
|
]
|
||||||
, actorSshKeys =
|
, AP.actorSshKeys =
|
||||||
map (encodeRouteLocal . SshKeyR shr . encodeKeyHashid) skids
|
map (encodeRouteLocal . SshKeyR personHash . hashSshKey) sshKeyIDs
|
||||||
}
|
}
|
||||||
, actorDetail = ActorDetail
|
, AP.actorDetail = AP.ActorDetail
|
||||||
{ actorType = ActorTypePerson
|
{ AP.actorType = AP.ActorTypePerson
|
||||||
, actorUsername = Just $ shr2text shr
|
, AP.actorUsername = Just $ username2text $ personUsername person
|
||||||
, actorName = sharerName sharer
|
, AP.actorName = Just $ actorName actor
|
||||||
, actorSummary = Nothing
|
, AP.actorSummary = Just $ actorDesc actor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
followButton =
|
||||||
|
followW
|
||||||
|
(PersonFollowR personHash)
|
||||||
|
(PersonUnfollowR personHash)
|
||||||
|
(actorFollowers actor)
|
||||||
|
|
||||||
|
let ep = Entity personID person
|
||||||
secure <- getSecure
|
secure <- getSecure
|
||||||
provideHtmlAndAP personAP $(widgetFile "person")
|
provideHtmlAndAP personAP $(widgetFile "person")
|
||||||
|
|
||||||
|
getPersonInboxR :: KeyHashid Person -> Handler TypedContent
|
||||||
|
getPersonInboxR = getInbox PersonInboxR personActor
|
||||||
|
|
||||||
|
postPersonInboxR :: KeyHashid Person -> Handler TypedContent
|
||||||
|
postPersonInboxR _ = error "Temporarily disabled"
|
||||||
|
|
||||||
|
getPersonOutboxR :: KeyHashid Person -> Handler TypedContent
|
||||||
|
getPersonOutboxR = getOutbox PersonOutboxR personActor
|
||||||
|
|
||||||
|
postPersonOutboxR :: KeyHashid Person -> Handler TypedContent
|
||||||
|
postPersonOutboxR personHash = do
|
||||||
|
federation <- getsYesod $ appFederation . appSettings
|
||||||
|
unless federation badMethod
|
||||||
|
|
||||||
|
personID <- decodeKeyHashid404 personHash
|
||||||
|
person <- runDB $ get404 personID
|
||||||
|
|
||||||
|
verifyPermission personID
|
||||||
|
verifyContentTypeAP
|
||||||
|
|
||||||
|
AP.Doc h activity <- requireInsecureJsonBody
|
||||||
|
hl <- hostIsLocal h
|
||||||
|
unless hl $ invalidArgs ["Activity host isn't the instance host"]
|
||||||
|
|
||||||
|
result <- runExceptT $ do
|
||||||
|
verifyAttribution $ AP.activityActor activity
|
||||||
|
handle (Entity personID person) activity
|
||||||
|
case result of
|
||||||
|
Left err -> invalidArgs [err]
|
||||||
|
Right outboxItemID -> do
|
||||||
|
outboxItemHash <- encodeKeyHashid outboxItemID
|
||||||
|
sendResponseCreated $ PersonOutboxItemR personHash outboxItemHash
|
||||||
where
|
where
|
||||||
followButton =
|
verifyPermission recipientID = do
|
||||||
followW
|
(_app, mpid, _scopes) <- maybe notAuthenticated return =<< getDvaraAuth
|
||||||
(SharerFollowR shr)
|
senderID <-
|
||||||
(SharerUnfollowR shr)
|
maybe (permissionDenied "Not authorized to post as a user") return mpid
|
||||||
(return $ personFollowers person)
|
unless (recipientID == senderID) $
|
||||||
|
permissionDenied "Can't post as other users"
|
||||||
|
|
||||||
|
verifyAttribution actor =
|
||||||
|
case decodeRouteLocal actor of
|
||||||
|
Just (PersonR actorHash) | actorHash == personHash -> return ()
|
||||||
|
_ -> throwE "Can't post activity attributed to someone else"
|
||||||
|
|
||||||
|
handle eperson (AP.Activity _mid actor mcap summary audience specific) =
|
||||||
|
case specific of
|
||||||
|
{-
|
||||||
|
AddActivity (AP.Add obj target) ->
|
||||||
|
case obj of
|
||||||
|
Right (AddBundle patches) ->
|
||||||
|
addBundleC eperson sharer summary audience patches target
|
||||||
|
_ -> throwE "Unsupported Add 'object' type"
|
||||||
|
ApplyActivity apply ->
|
||||||
|
applyC eperson sharer summary audience mcap apply
|
||||||
|
CreateActivity (Create obj mtarget) ->
|
||||||
|
case obj of
|
||||||
|
CreateNote _ note ->
|
||||||
|
createNoteC eperson sharer summary audience note mtarget
|
||||||
|
CreateTicket _ ticket ->
|
||||||
|
createTicketC eperson sharer summary audience ticket mtarget
|
||||||
|
_ -> throwE "Unsupported Create 'object' type"
|
||||||
|
FollowActivity follow ->
|
||||||
|
followC shr summary audience follow
|
||||||
|
OfferActivity (Offer obj target) ->
|
||||||
|
case obj of
|
||||||
|
OfferTicket ticket ->
|
||||||
|
offerTicketC eperson sharer summary audience ticket target
|
||||||
|
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 eperson sharer summary audience undo
|
||||||
|
-}
|
||||||
|
_ -> throwE "Unsupported activity type"
|
||||||
|
|
||||||
|
getPersonOutboxItemR
|
||||||
|
:: KeyHashid Person -> KeyHashid OutboxItem -> Handler TypedContent
|
||||||
|
getPersonOutboxItemR = getOutboxItem PersonOutboxItemR personActor
|
||||||
|
|
||||||
|
getPersonFollowersR :: KeyHashid Person -> Handler TypedContent
|
||||||
|
getPersonFollowersR = getActorFollowersCollection PersonFollowersR personActor
|
||||||
|
|
||||||
|
getPersonFollowingR :: KeyHashid Person -> Handler TypedContent
|
||||||
|
getPersonFollowingR = getFollowingCollection PersonFollowingR personActor
|
||||||
|
|
||||||
|
getSshKeyR :: KeyHashid Person -> KeyHashid SshKey -> Handler TypedContent
|
||||||
|
getSshKeyR personHash keyHash = do
|
||||||
|
personID <- decodeKeyHashid404 personHash
|
||||||
|
keyID <- decodeKeyHashid404 keyHash
|
||||||
|
key <- runDB $ do
|
||||||
|
_ <- get404 personID
|
||||||
|
k <- get404 keyID
|
||||||
|
unless (sshKeyPerson k == personID) notFound
|
||||||
|
return k
|
||||||
|
|
||||||
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
|
let here = SshKeyR personHash keyHash
|
||||||
|
keyAP = AP.SshPublicKey
|
||||||
|
{ AP.sshPublicKeyId = encodeRouteLocal here
|
||||||
|
, AP.sshPublicKeyExpires = Nothing
|
||||||
|
, AP.sshPublicKeyOwner = encodeRouteLocal $ PersonR personHash
|
||||||
|
, AP.sshPublicKeyAlgorithm =
|
||||||
|
case sshKeyAlgo key of
|
||||||
|
"ssh-rsa" -> AP.SshKeyAlgorithmRSA
|
||||||
|
_ -> error "Unexpected sshKeyAlgo in DB"
|
||||||
|
, AP.sshPublicKeyMaterial = sshKeyContent key
|
||||||
|
}
|
||||||
|
provideHtmlAndAP keyAP $ redirectToPrettyJSON here
|
||||||
|
|
||||||
|
getPersonMessageR
|
||||||
|
:: KeyHashid Person -> KeyHashid LocalMessage -> Handler TypedContent
|
||||||
|
getPersonMessageR personHash localMessageHash = do
|
||||||
|
personID <- decodeKeyHashid404 personHash
|
||||||
|
localMessageID <- decodeKeyHashid404 localMessageHash
|
||||||
|
|
||||||
|
encodeRouteHome <- getEncodeRouteHome
|
||||||
|
workItemRoute <- askWorkItemRoute
|
||||||
|
note <- runDB $ do
|
||||||
|
_ <- get404 personID
|
||||||
|
localMessage <- get404 localMessageID
|
||||||
|
unless (localMessageAuthor localMessage == personID) notFound
|
||||||
|
message <- getJust $ localMessageRest localMessage
|
||||||
|
|
||||||
|
uContext <- do
|
||||||
|
let discussionID = messageRoot message
|
||||||
|
topic <-
|
||||||
|
requireEitherAlt
|
||||||
|
(getKeyBy $ UniqueTicketDiscuss discussionID)
|
||||||
|
(getValBy $ UniqueRemoteDiscussion discussionID)
|
||||||
|
"Neither T nor RD found"
|
||||||
|
"Both T and RD found"
|
||||||
|
case topic of
|
||||||
|
Left ticketID ->
|
||||||
|
encodeRouteHome . workItemRoute <$> getWorkItem ticketID
|
||||||
|
Right rd -> do
|
||||||
|
ro <- getJust $ remoteDiscussionIdent rd
|
||||||
|
i <- getJust $ remoteObjectInstance ro
|
||||||
|
return $ ObjURI (instanceHost i) (remoteObjectIdent ro)
|
||||||
|
|
||||||
|
muParent <- for (messageParent message) $ \ parentID -> do
|
||||||
|
parent <-
|
||||||
|
requireEitherAlt
|
||||||
|
(getBy $ UniqueLocalMessage parentID)
|
||||||
|
(getValBy $ UniqueRemoteMessage parentID)
|
||||||
|
"Message with no author"
|
||||||
|
"Message used as both local and remote"
|
||||||
|
case parent of
|
||||||
|
Left (Entity localParentID localParent) -> do
|
||||||
|
authorHash <-
|
||||||
|
encodeKeyHashid $ localMessageAuthor localParent
|
||||||
|
localParentHash <- encodeKeyHashid localParentID
|
||||||
|
return $ encodeRouteHome $
|
||||||
|
PersonMessageR authorHash localParentHash
|
||||||
|
Right remoteParent -> do
|
||||||
|
rs <- getJust $ remoteMessageAuthor remoteParent
|
||||||
|
ro <- getJust $ remoteActorIdent rs
|
||||||
|
i <- getJust $ remoteObjectInstance ro
|
||||||
|
return $ ObjURI (instanceHost i) (remoteObjectIdent ro)
|
||||||
|
|
||||||
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
|
return AP.Note
|
||||||
|
{ AP.noteId = Just $ encodeRouteLocal here
|
||||||
|
, AP.noteAttrib = encodeRouteLocal $ PersonR personHash
|
||||||
|
, AP.noteAudience = AP.Audience [] [] [] [] [] []
|
||||||
|
, AP.noteReplyTo = Just $ fromMaybe uContext muParent
|
||||||
|
, AP.noteContext = Just uContext
|
||||||
|
, AP.notePublished = Just $ messageCreated message
|
||||||
|
, AP.noteSource = messageSource message
|
||||||
|
, AP.noteContent = messageContent message
|
||||||
|
}
|
||||||
|
provideHtmlAndAP note $ redirectToPrettyJSON here
|
||||||
|
where
|
||||||
|
here = PersonMessageR personHash localMessageHash
|
||||||
|
|
||||||
|
postPersonFollowR :: KeyHashid Person -> Handler ()
|
||||||
|
postPersonFollowR _ = error "Temporarily disabled"
|
||||||
|
|
||||||
|
postPersonUnfollowR :: KeyHashid Person -> Handler ()
|
||||||
|
postPersonUnfollowR _ = error "Temporarily disabled"
|
||||||
|
|
|
@ -15,42 +15,67 @@
|
||||||
-}
|
-}
|
||||||
|
|
||||||
module Vervis.Handler.Repo
|
module Vervis.Handler.Repo
|
||||||
( getReposR
|
( getRepoR
|
||||||
, postReposR
|
, getRepoInboxR
|
||||||
, getRepoNewR
|
, postRepoInboxR
|
||||||
, getRepoR
|
, getRepoOutboxR
|
||||||
, putRepoR
|
, getRepoOutboxItemR
|
||||||
, deleteRepoR
|
, getRepoFollowersR
|
||||||
, postRepoR
|
|
||||||
, getRepoEditR
|
, getDarcsDownloadR
|
||||||
|
, getGitRefDiscoverR
|
||||||
|
, postGitUploadRequestR
|
||||||
|
|
||||||
, getRepoSourceR
|
, getRepoSourceR
|
||||||
, getRepoHeadChangesR
|
, getRepoBranchSourceR
|
||||||
, getRepoBranchR
|
, getRepoCommitsR
|
||||||
, getRepoChangesR
|
, getRepoBranchCommitsR
|
||||||
, getRepoCommitR
|
, getRepoCommitR
|
||||||
|
|
||||||
|
, getRepoNewR
|
||||||
|
, postRepoNewR
|
||||||
|
, postRepoDeleteR
|
||||||
|
, getRepoEditR
|
||||||
|
, postRepoEditR
|
||||||
|
, postRepoFollowR
|
||||||
|
, postRepoUnfollowR
|
||||||
|
|
||||||
|
, postPostReceiveR
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{-
|
||||||
|
, getReposR
|
||||||
|
, putRepoR
|
||||||
|
, postRepoR
|
||||||
|
, getRepoBranchR
|
||||||
, getRepoDevsR
|
, getRepoDevsR
|
||||||
, postRepoDevsR
|
, postRepoDevsR
|
||||||
, getRepoDevNewR
|
, getRepoDevNewR
|
||||||
, getRepoDevR
|
, getRepoDevR
|
||||||
, deleteRepoDevR
|
, deleteRepoDevR
|
||||||
, postRepoDevR
|
, postRepoDevR
|
||||||
, getDarcsDownloadR
|
|
||||||
, getRepoTeamR
|
, getRepoTeamR
|
||||||
, getRepoFollowersR
|
|
||||||
|
|
||||||
, getHighlightStyleR
|
, getHighlightStyleR
|
||||||
, postPostReceiveR
|
-}
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
import Control.Exception hiding (Handler)
|
import Control.Exception hiding (Handler)
|
||||||
|
import Control.Monad
|
||||||
import Control.Monad.IO.Class (liftIO)
|
import Control.Monad.IO.Class (liftIO)
|
||||||
import Control.Monad.Logger (logWarn)
|
import Control.Monad.Logger (logWarn)
|
||||||
import Control.Monad.Trans.Except
|
import Control.Monad.Trans.Except
|
||||||
import Data.Bifunctor
|
import Data.Bifunctor
|
||||||
|
import Data.Binary.Put
|
||||||
import Data.Foldable
|
import Data.Foldable
|
||||||
import Data.Git.Graph
|
import Data.Git.Graph
|
||||||
import Data.Git.Harder
|
import Data.Git.Harder
|
||||||
|
import Data.Git.Harder.Pack
|
||||||
import Data.Git.Named (RefName (..))
|
import Data.Git.Named (RefName (..))
|
||||||
import Data.Git.Ref (toHex)
|
import Data.Git.Ref (toHex)
|
||||||
import Data.Git.Repository
|
import Data.Git.Repository
|
||||||
|
@ -60,6 +85,8 @@ import Data.Git.Types (Blob (..), Person (..), entName)
|
||||||
import Data.Graph.Inductive.Graph (noNodes)
|
import Data.Graph.Inductive.Graph (noNodes)
|
||||||
import Data.Graph.Inductive.Query.Topsort
|
import Data.Graph.Inductive.Query.Topsort
|
||||||
import Data.List (inits)
|
import Data.List (inits)
|
||||||
|
import Data.Maybe
|
||||||
|
import Data.String
|
||||||
import Data.Text (Text, unpack)
|
import Data.Text (Text, unpack)
|
||||||
import Data.Text.Encoding
|
import Data.Text.Encoding
|
||||||
import Data.Text.Encoding.Error (lenientDecode)
|
import Data.Text.Encoding.Error (lenientDecode)
|
||||||
|
@ -69,19 +96,28 @@ import Database.Persist
|
||||||
import Database.Persist.Sql
|
import Database.Persist.Sql
|
||||||
import Data.Hourglass (timeConvert)
|
import Data.Hourglass (timeConvert)
|
||||||
import Formatting (sformat, stext, (%))
|
import Formatting (sformat, stext, (%))
|
||||||
|
import Network.Git.Transport.HTTP.Fetch.RefDiscovery
|
||||||
|
import Network.Git.Transport.HTTP.Fetch.UploadRequest
|
||||||
|
import Network.Git.Types
|
||||||
|
import Network.Wai (strictRequestBody)
|
||||||
import System.Directory
|
import System.Directory
|
||||||
|
import System.FilePath
|
||||||
import System.Hourglass (dateCurrent)
|
import System.Hourglass (dateCurrent)
|
||||||
|
import System.IO
|
||||||
|
import System.Process
|
||||||
import Text.Blaze.Html (Html)
|
import Text.Blaze.Html (Html)
|
||||||
import Text.Pandoc.Highlighting
|
import Text.Pandoc.Highlighting
|
||||||
import Yesod.Auth (requireAuthId)
|
import Yesod.Auth (requireAuthId)
|
||||||
import Yesod.Core
|
import Yesod.Core hiding (joinPath)
|
||||||
import Yesod.Core.Content
|
import Yesod.Core.Content
|
||||||
import Yesod.Core.Handler (lookupPostParam, redirect, notFound)
|
import Yesod.Core.Handler (lookupPostParam, redirect, notFound)
|
||||||
import Yesod.Form.Functions (runFormPost)
|
import Yesod.Form.Functions (runFormPost)
|
||||||
import Yesod.Form.Types (FormResult (..))
|
import Yesod.Form.Types (FormResult (..))
|
||||||
import Yesod.Persist.Core
|
import Yesod.Persist.Core
|
||||||
|
|
||||||
import qualified Data.ByteString.Lazy as BL (ByteString)
|
import qualified Data.ByteString as B
|
||||||
|
import qualified Data.ByteString.Lazy as BL
|
||||||
|
import qualified Data.ByteString.Lazy.Internal as BLI
|
||||||
import qualified Data.CaseInsensitive as CI (foldedCase)
|
import qualified Data.CaseInsensitive as CI (foldedCase)
|
||||||
import qualified Data.DList as D
|
import qualified Data.DList as D
|
||||||
import qualified Data.Set as S (member)
|
import qualified Data.Set as S (member)
|
||||||
|
@ -91,8 +127,8 @@ import qualified Database.Esqueleto as E
|
||||||
|
|
||||||
import Data.MediaType
|
import Data.MediaType
|
||||||
import Database.Persist.JSON
|
import Database.Persist.JSON
|
||||||
|
import Development.PatchMediaType
|
||||||
import Network.FedURI
|
import Network.FedURI
|
||||||
import Web.ActivityPub hiding (Repo (..), Project)
|
|
||||||
import Yesod.ActivityPub
|
import Yesod.ActivityPub
|
||||||
import Yesod.FedURI
|
import Yesod.FedURI
|
||||||
import Yesod.Hashids
|
import Yesod.Hashids
|
||||||
|
@ -111,38 +147,220 @@ import Yesod.Persist.Local
|
||||||
import qualified Data.Git.Local as G (createRepo)
|
import qualified Data.Git.Local as G (createRepo)
|
||||||
import qualified Darcs.Local.Repository as D (createRepo)
|
import qualified Darcs.Local.Repository as D (createRepo)
|
||||||
|
|
||||||
|
import Vervis.Actor
|
||||||
import Vervis.API
|
import Vervis.API
|
||||||
import Vervis.Form.Repo
|
|
||||||
import Vervis.Foundation
|
import Vervis.Foundation
|
||||||
import Vervis.Handler.Repo.Darcs
|
|
||||||
import Vervis.Handler.Repo.Git
|
|
||||||
import Vervis.Path
|
import Vervis.Path
|
||||||
import Vervis.Model hiding (Actor (..))
|
import Vervis.Model
|
||||||
import Vervis.Model.Ident
|
import Vervis.Model.Ident
|
||||||
import Development.PatchMediaType
|
|
||||||
import Vervis.Paginate
|
import Vervis.Paginate
|
||||||
import Vervis.Readme
|
import Vervis.Readme
|
||||||
import Vervis.Settings
|
import Vervis.Settings
|
||||||
import Vervis.SourceTree
|
import Vervis.SourceTree
|
||||||
import Vervis.Style
|
import Vervis.Style
|
||||||
import Vervis.Widget.Repo
|
|
||||||
import Vervis.Widget.Sharer
|
|
||||||
|
|
||||||
import qualified Vervis.Formatting as F
|
import qualified Vervis.Formatting as F
|
||||||
import qualified Vervis.Hook as H
|
import qualified Vervis.Hook as H
|
||||||
|
|
||||||
getReposR :: ShrIdent -> Handler Html
|
getRepoR :: KeyHashid Repo -> Handler TypedContent
|
||||||
getReposR user = do
|
getRepoR repoHash = do
|
||||||
repos <- runDB $ E.select $ E.from $ \ (sharer, repo) -> do
|
repoID <- decodeKeyHashid404 repoHash
|
||||||
E.where_ $
|
(repo, actor) <- runDB $ do
|
||||||
sharer E.^. SharerIdent E.==. E.val user E.&&.
|
r <- get404 repoID
|
||||||
sharer E.^. SharerId E.==. repo E.^. RepoSharer
|
(r,) <$> getJust (repoActor r)
|
||||||
E.orderBy [E.asc $ repo E.^. RepoIdent]
|
|
||||||
return $ repo E.^. RepoIdent
|
|
||||||
defaultLayout $(widgetFile "repo/list")
|
|
||||||
|
|
||||||
postReposR :: ShrIdent -> Handler Html
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
postReposR user = do
|
let repoAP = AP.Repo
|
||||||
|
{ AP.repoActor = AP.Actor
|
||||||
|
{ AP.actorLocal = AP.ActorLocal
|
||||||
|
{ AP.actorId = encodeRouteLocal $ RepoR repoHash
|
||||||
|
, AP.actorInbox = encodeRouteLocal $ RepoInboxR repoHash
|
||||||
|
, AP.actorOutbox =
|
||||||
|
Just $ encodeRouteLocal $ RepoOutboxR repoHash
|
||||||
|
, AP.actorFollowers =
|
||||||
|
Just $ encodeRouteLocal $ RepoFollowersR repoHash
|
||||||
|
, AP.actorFollowing = Nothing
|
||||||
|
, AP.actorPublicKeys =
|
||||||
|
[ Left $ encodeRouteLocal ActorKey1R
|
||||||
|
, Left $ encodeRouteLocal ActorKey2R
|
||||||
|
]
|
||||||
|
, AP.actorSshKeys = []
|
||||||
|
}
|
||||||
|
, AP.actorDetail = AP.ActorDetail
|
||||||
|
{ AP.actorType = AP.ActorTypeRepo
|
||||||
|
, AP.actorUsername = Nothing
|
||||||
|
, AP.actorName = Just $ actorName actor
|
||||||
|
, AP.actorSummary = Just $ actorDesc actor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
, AP.repoTeam = Nothing
|
||||||
|
, AP.repoVcs = repoVcs repo
|
||||||
|
}
|
||||||
|
|
||||||
|
next =
|
||||||
|
case repoVcs repo of
|
||||||
|
VCSDarcs -> RepoSourceR repoHash
|
||||||
|
VCSGit -> RepoBranchSourceR repoHash $ repoMainBranch repo
|
||||||
|
provideHtmlAndAP repoAP $ redirect $ next []
|
||||||
|
|
||||||
|
getRepoInboxR :: KeyHashid Repo -> Handler TypedContent
|
||||||
|
getRepoInboxR = getInbox RepoInboxR repoActor
|
||||||
|
|
||||||
|
postRepoInboxR :: KeyHashid Repo -> Handler TypedContent
|
||||||
|
postRepoInboxR _ = error "Temporarily disabled"
|
||||||
|
|
||||||
|
getRepoOutboxR :: KeyHashid Repo -> Handler TypedContent
|
||||||
|
getRepoOutboxR = getOutbox RepoOutboxR repoActor
|
||||||
|
|
||||||
|
getRepoOutboxItemR
|
||||||
|
:: KeyHashid Repo -> KeyHashid OutboxItem -> Handler TypedContent
|
||||||
|
getRepoOutboxItemR = getOutboxItem RepoOutboxItemR repoActor
|
||||||
|
|
||||||
|
getRepoFollowersR :: KeyHashid Repo -> Handler TypedContent
|
||||||
|
getRepoFollowersR = getActorFollowersCollection RepoFollowersR repoActor
|
||||||
|
|
||||||
|
getDarcsDownloadR :: KeyHashid Repo -> [Text] -> Handler TypedContent
|
||||||
|
getDarcsDownloadR repoHash dir = do
|
||||||
|
repoPath <- askRepoDir repoHash
|
||||||
|
let filePath = repoPath </> "_darcs" </> joinPath (map T.unpack dir)
|
||||||
|
exists <- liftIO $ doesFileExist filePath
|
||||||
|
if exists
|
||||||
|
then sendFile typeOctet filePath
|
||||||
|
else notFound
|
||||||
|
|
||||||
|
getGitRefDiscoverR :: KeyHashid Repo -> Handler TypedContent
|
||||||
|
getGitRefDiscoverR repoHash = do
|
||||||
|
let typ = "application/x-git-upload-pack-advertisement"
|
||||||
|
path <- askRepoDir repoHash
|
||||||
|
let pathG = fromString path
|
||||||
|
seemsThere <- liftIO $ isRepo pathG
|
||||||
|
if seemsThere
|
||||||
|
then do
|
||||||
|
rq <- getRequest
|
||||||
|
case reqGetParams rq of
|
||||||
|
[("service", serv)] ->
|
||||||
|
if serv == "git-upload-pack"
|
||||||
|
then do
|
||||||
|
let settings =
|
||||||
|
( proc "git"
|
||||||
|
[ "upload-pack"
|
||||||
|
, "--stateless-rpc"
|
||||||
|
, "--advertise-refs"
|
||||||
|
, path
|
||||||
|
]
|
||||||
|
)
|
||||||
|
{ std_out = CreatePipe
|
||||||
|
}
|
||||||
|
(_, mh, _, _) <-
|
||||||
|
liftIO $ createProcess settings
|
||||||
|
let h = fromJust mh
|
||||||
|
refs <- liftIO $ B.hGetContents h
|
||||||
|
let content = runPut $ do
|
||||||
|
putService UploadPack
|
||||||
|
putByteString refs
|
||||||
|
setHeader "Cache-Control" "no-cache"
|
||||||
|
return $ TypedContent typ $ toContent content
|
||||||
|
else permissionDenied "Service not supported"
|
||||||
|
_ -> notFound
|
||||||
|
else notFound
|
||||||
|
|
||||||
|
postGitUploadRequestR :: KeyHashid Repo -> Handler TypedContent
|
||||||
|
postGitUploadRequestR repoHash = do
|
||||||
|
let typ = "application/x-git-upload-pack-result"
|
||||||
|
path <- askRepoDir repoHash
|
||||||
|
let pathG = fromString path
|
||||||
|
seemsThere <- liftIO $ isRepo pathG
|
||||||
|
if seemsThere
|
||||||
|
then do
|
||||||
|
getBody <- strictRequestBody <$> waiRequest
|
||||||
|
body <- liftIO getBody
|
||||||
|
let settings =
|
||||||
|
( proc "git"
|
||||||
|
[ "upload-pack"
|
||||||
|
, "--stateless-rpc"
|
||||||
|
, path
|
||||||
|
]
|
||||||
|
)
|
||||||
|
{ std_in = CreatePipe
|
||||||
|
, std_out = CreatePipe
|
||||||
|
}
|
||||||
|
(mhin, mhout, _, _) <- liftIO $ createProcess settings
|
||||||
|
let hin = fromJust mhin
|
||||||
|
hout = fromJust mhout
|
||||||
|
liftIO $ BL.hPut hin body >> hClose hin
|
||||||
|
setHeader "Cache-Control" "no-cache"
|
||||||
|
let loop = do
|
||||||
|
b <- liftIO $ B.hGet hout BLI.defaultChunkSize
|
||||||
|
unless (B.null b) $ do
|
||||||
|
sendChunkBS b
|
||||||
|
loop
|
||||||
|
respondSource typ loop
|
||||||
|
else notFound
|
||||||
|
|
||||||
|
getRepoSourceR :: KeyHashid Repo -> [Text] -> Handler Html
|
||||||
|
getRepoSourceR repoHash path = do
|
||||||
|
repoID <- decodeKeyHashid404 repoHash
|
||||||
|
repo <- runDB $ get404 repoID
|
||||||
|
case repoVcs repo of
|
||||||
|
VCSDarcs -> error "Temporarily disabled"
|
||||||
|
--getDarcsRepoSource repo repoHash path
|
||||||
|
VCSGit -> notFound
|
||||||
|
|
||||||
|
getRepoBranchSourceR :: KeyHashid Repo -> Text -> [Text] -> Handler Html
|
||||||
|
getRepoBranchSourceR repoHash branch path = do
|
||||||
|
repoID <- decodeKeyHashid404 repoHash
|
||||||
|
repo <- runDB $ get404 repoID
|
||||||
|
case repoVcs repo of
|
||||||
|
VCSDarcs -> notFound
|
||||||
|
VCSGit -> error "Temporarily disabled"
|
||||||
|
--getGitRepoSource repo repoHash branch dir
|
||||||
|
|
||||||
|
getRepoCommitsR :: KeyHashid Repo -> Handler TypedContent
|
||||||
|
getRepoCommitsR repoHash = do
|
||||||
|
repoID <- decodeKeyHashid404 repoHash
|
||||||
|
repo <- runDB $ get404 repoID
|
||||||
|
case repoVcs repo of
|
||||||
|
VCSDarcs ->
|
||||||
|
error "Temporarily disabled"
|
||||||
|
--getDarcsRepoHeadChanges repoHash
|
||||||
|
VCSGit ->
|
||||||
|
error "Temporarily disabled"
|
||||||
|
--getGitRepoHeadChanges repo repoHash
|
||||||
|
|
||||||
|
getRepoBranchCommitsR :: KeyHashid Repo -> Text -> Handler TypedContent
|
||||||
|
getRepoBranchCommitsR repoHash branch = do
|
||||||
|
repoID <- decodeKeyHashid404 repoHash
|
||||||
|
repo <- runDB $ get404 repoID
|
||||||
|
case repoVcs repo of
|
||||||
|
VCSDarcs ->
|
||||||
|
error "Temporarily disabled"
|
||||||
|
--getDarcsRepoChanges repoHash branch
|
||||||
|
VCSGit ->
|
||||||
|
error "Temporarily disabled"
|
||||||
|
--getGitRepoChanges repoHash branch
|
||||||
|
|
||||||
|
getRepoCommitR :: KeyHashid Repo -> Text -> Handler TypedContent
|
||||||
|
getRepoCommitR repoHash ref = do
|
||||||
|
error "Temporarily disabled"
|
||||||
|
{-
|
||||||
|
repoID <- decodeKeyHashid404 repoHash
|
||||||
|
repo <- runDB $ get404 repoID
|
||||||
|
case repoVcs repo of
|
||||||
|
VCSDarcs -> getDarcsPatch repoHash ref
|
||||||
|
VCSGit -> getGitPatch repoHash ref
|
||||||
|
-}
|
||||||
|
|
||||||
|
getRepoNewR :: Handler Html
|
||||||
|
getRepoNewR = do
|
||||||
|
error "Temporarily disabled"
|
||||||
|
--Entity sid _sharer <- runDB $ getBy404 $ UniqueSharer user
|
||||||
|
--((_result, widget), enctype) <- runFormPost $ newRepoForm sid Nothing
|
||||||
|
--defaultLayout $(widgetFile "repo/new")
|
||||||
|
|
||||||
|
postRepoNewR :: Handler Html
|
||||||
|
postRepoNewR = do
|
||||||
|
error "Temporarily disabled"
|
||||||
|
{-
|
||||||
Entity sid _sharer <- runDB $ getBy404 $ UniqueSharer user
|
Entity sid _sharer <- runDB $ getBy404 $ UniqueSharer user
|
||||||
((result, widget), enctype) <- runFormPost $ newRepoForm sid Nothing
|
((result, widget), enctype) <- runFormPost $ newRepoForm sid Nothing
|
||||||
case result of
|
case result of
|
||||||
|
@ -213,63 +431,46 @@ postReposR user = do
|
||||||
FormFailure _l -> do
|
FormFailure _l -> do
|
||||||
setMessage "Repo creation failed, see errors below"
|
setMessage "Repo creation failed, see errors below"
|
||||||
defaultLayout $(widgetFile "repo/new")
|
defaultLayout $(widgetFile "repo/new")
|
||||||
|
-}
|
||||||
|
|
||||||
getRepoNewR :: ShrIdent -> Handler Html
|
postRepoDeleteR :: KeyHashid Repo -> Handler Html
|
||||||
getRepoNewR user = do
|
postRepoDeleteR repoHash = do
|
||||||
Entity sid _sharer <- runDB $ getBy404 $ UniqueSharer user
|
error "Temporarily disabled"
|
||||||
((_result, widget), enctype) <- runFormPost $ newRepoForm sid Nothing
|
{-
|
||||||
defaultLayout $(widgetFile "repo/new")
|
runDB $ do
|
||||||
|
Entity sid _s <- getBy404 $ UniqueSharer shar
|
||||||
|
Entity rid _r <- getBy404 $ UniqueRepo repo sid
|
||||||
|
delete rid
|
||||||
|
path <- askRepoDir shar repo
|
||||||
|
exists <- liftIO $ doesDirectoryExist path
|
||||||
|
if exists
|
||||||
|
then liftIO $ removeDirectoryRecursive path
|
||||||
|
else
|
||||||
|
$logWarn $ sformat
|
||||||
|
( "Deleted repo " % F.sharer % "/" % F.repo
|
||||||
|
% " from DB but repo dir doesn't exist"
|
||||||
|
)
|
||||||
|
shar repo
|
||||||
|
setMessage "Repo deleted."
|
||||||
|
redirect HomeR
|
||||||
|
-}
|
||||||
|
|
||||||
selectRepo :: ShrIdent -> RpIdent -> AppDB (Maybe (Sharer, Project, Workflow, Sharer), Repo)
|
getRepoEditR :: KeyHashid Repo -> Handler Html
|
||||||
selectRepo shar repo = do
|
getRepoEditR repoHash = do
|
||||||
Entity sid _s <- getBy404 $ UniqueSharer shar
|
error "Temporarily disabled"
|
||||||
Entity _rid r <- getBy404 $ UniqueRepo repo sid
|
{-
|
||||||
mj <- for (repoProject r) $ \ jid -> do
|
(sid, er) <- runDB $ do
|
||||||
j <- get404 jid
|
Entity sid _ <- getBy404 $ UniqueSharer shr
|
||||||
s <- get404 $ projectSharer j
|
er <- getBy404 $ UniqueRepo rp sid
|
||||||
w <- get404 $ projectWorkflow j
|
return (sid, er)
|
||||||
sw <- get404 $ workflowSharer w
|
((_result, widget), enctype) <- runFormPost $ editRepoForm sid er
|
||||||
return (s, j, w, sw)
|
defaultLayout $(widgetFile "repo/edit")
|
||||||
return (mj, r)
|
-}
|
||||||
|
|
||||||
getRepoR :: ShrIdent -> RpIdent -> Handler TypedContent
|
postRepoEditR :: KeyHashid Repo -> Handler Html
|
||||||
getRepoR shr rp = do
|
postRepoEditR repoHash = do
|
||||||
(_, repo) <- runDB $ selectRepo shr rp
|
error "Temporarily disabled"
|
||||||
encodeRouteLocal <- getEncodeRouteLocal
|
{-
|
||||||
encodeRouteHome <- getEncodeRouteHome
|
|
||||||
let repoAP = AP.Repo
|
|
||||||
{ AP.repoActor = Actor
|
|
||||||
{ actorLocal = ActorLocal
|
|
||||||
{ actorId = encodeRouteLocal $ RepoR shr rp
|
|
||||||
, actorInbox = encodeRouteLocal $ RepoInboxR shr rp
|
|
||||||
, actorOutbox =
|
|
||||||
Just $ encodeRouteLocal $ RepoOutboxR shr rp
|
|
||||||
, actorFollowers =
|
|
||||||
Just $ encodeRouteLocal $ RepoFollowersR shr rp
|
|
||||||
, actorFollowing = Nothing
|
|
||||||
, actorPublicKeys =
|
|
||||||
[ Left $ encodeRouteLocal ActorKey1R
|
|
||||||
, Left $ encodeRouteLocal ActorKey2R
|
|
||||||
]
|
|
||||||
, actorSshKeys = []
|
|
||||||
}
|
|
||||||
, actorDetail = ActorDetail
|
|
||||||
{ actorType = ActorTypeRepo
|
|
||||||
, actorUsername = Nothing
|
|
||||||
, actorName = Just $ rp2text rp
|
|
||||||
, actorSummary = repoDesc repo
|
|
||||||
}
|
|
||||||
}
|
|
||||||
, AP.repoTeam = encodeRouteLocal $ RepoTeamR shr rp
|
|
||||||
, AP.repoVcs = repoVcs repo
|
|
||||||
}
|
|
||||||
dir = case repoVcs repo of
|
|
||||||
VCSDarcs -> []
|
|
||||||
VCSGit -> [repoMainBranch repo]
|
|
||||||
provideHtmlAndAP repoAP $ redirect $ RepoSourceR shr rp dir
|
|
||||||
|
|
||||||
putRepoR :: ShrIdent -> RpIdent -> Handler Html
|
|
||||||
putRepoR shr rp = do
|
|
||||||
mer <- runDB $ do
|
mer <- runDB $ do
|
||||||
Entity sid _ <- getBy404 $ UniqueSharer shr
|
Entity sid _ <- getBy404 $ UniqueSharer shr
|
||||||
er@(Entity rid r) <- getBy404 $ UniqueRepo rp sid
|
er@(Entity rid r) <- getBy404 $ UniqueRepo rp sid
|
||||||
|
@ -296,58 +497,184 @@ putRepoR shr rp = do
|
||||||
FormFailure _l -> do
|
FormFailure _l -> do
|
||||||
setMessage "Repository update failed, see errors below."
|
setMessage "Repository update failed, see errors below."
|
||||||
defaultLayout $(widgetFile "repo/edit")
|
defaultLayout $(widgetFile "repo/edit")
|
||||||
|
-}
|
||||||
|
|
||||||
deleteRepoR :: ShrIdent -> RpIdent -> Handler Html
|
postRepoFollowR :: KeyHashid Repo -> Handler ()
|
||||||
deleteRepoR shar repo = do
|
postRepoFollowR _ = error "Temporarily disabled"
|
||||||
runDB $ do
|
|
||||||
Entity sid _s <- getBy404 $ UniqueSharer shar
|
|
||||||
Entity rid _r <- getBy404 $ UniqueRepo repo sid
|
|
||||||
delete rid
|
|
||||||
path <- askRepoDir shar repo
|
|
||||||
exists <- liftIO $ doesDirectoryExist path
|
|
||||||
if exists
|
|
||||||
then liftIO $ removeDirectoryRecursive path
|
|
||||||
else
|
|
||||||
$logWarn $ sformat
|
|
||||||
( "Deleted repo " % F.sharer % "/" % F.repo
|
|
||||||
% " from DB but repo dir doesn't exist"
|
|
||||||
)
|
|
||||||
shar repo
|
|
||||||
setMessage "Repo deleted."
|
|
||||||
redirect HomeR
|
|
||||||
|
|
||||||
postRepoR :: ShrIdent -> RpIdent -> Handler Html
|
postRepoUnfollowR :: KeyHashid Repo -> Handler ()
|
||||||
postRepoR shar repo = do
|
postRepoUnfollowR _ = error "Temporarily disabled"
|
||||||
mmethod <- lookupPostParam "_method"
|
|
||||||
case mmethod of
|
|
||||||
Just "PUT" -> putRepoR shar repo
|
|
||||||
Just "DELETE" -> deleteRepoR shar repo
|
|
||||||
_ -> notFound
|
|
||||||
|
|
||||||
getRepoEditR :: ShrIdent -> RpIdent -> Handler Html
|
postPostReceiveR :: Handler Text
|
||||||
getRepoEditR shr rp = do
|
postPostReceiveR = do
|
||||||
(sid, er) <- runDB $ do
|
error "Temporarily disabled"
|
||||||
Entity sid _ <- getBy404 $ UniqueSharer shr
|
{-
|
||||||
er <- getBy404 $ UniqueRepo rp sid
|
push <- requireCheckJsonBody
|
||||||
return (sid, er)
|
(pushAP, shr, rp) <- push2ap push
|
||||||
((_result, widget), enctype) <- runFormPost $ editRepoForm sid er
|
user <- runDB $ do
|
||||||
defaultLayout $(widgetFile "repo/edit")
|
p <- getJustEntity $ toSqlKey $ H.pushUser push
|
||||||
|
s <- getJust $ personIdent $ entityVal p
|
||||||
|
return (p, s)
|
||||||
|
let shrUser = sharerIdent $ snd user
|
||||||
|
summary <- do
|
||||||
|
let mbranch = H.pushBranch push
|
||||||
|
total = pushCommitsTotal pushAP
|
||||||
|
lasts = pushCommitsLast pushAP
|
||||||
|
rest firsts = total - length firsts - length lasts
|
||||||
|
hashText (Hash b) = decodeUtf8 b
|
||||||
|
commitW c =
|
||||||
|
[hamlet|
|
||||||
|
<a href=@{RepoCommitR shr rp $ hashText $ commitHash c}>
|
||||||
|
#{commitTitle c}
|
||||||
|
|]
|
||||||
|
withUrlRenderer
|
||||||
|
[hamlet|
|
||||||
|
<p>
|
||||||
|
<a href=@{SharerR shrUser}>#{shr2text shrUser}
|
||||||
|
\ pushed #{total} #
|
||||||
|
\ #{commitsText mbranch total} to repo #
|
||||||
|
<a href=@{RepoR shr rp}>#{rp2text rp}</a>^{branchText shr rp mbranch}:
|
||||||
|
<ul>
|
||||||
|
$maybe firsts <- pushCommitsFirst pushAP
|
||||||
|
$forall c <- firsts
|
||||||
|
<li>^{commitW c}
|
||||||
|
<li>#{rest firsts}
|
||||||
|
$forall c <- lasts
|
||||||
|
<li>^{commitW c}
|
||||||
|
|]
|
||||||
|
eid <- runExceptT $ pushCommitsC user summary pushAP shr rp
|
||||||
|
case eid of
|
||||||
|
Left e -> liftIO $ throwIO $ userError $ T.unpack e
|
||||||
|
Right obiid -> do
|
||||||
|
renderUrl <- askUrlRender
|
||||||
|
obikhid <- encodeKeyHashid obiid
|
||||||
|
return $
|
||||||
|
"Push activity published: " <>
|
||||||
|
renderUrl (SharerOutboxItemR shrUser obikhid)
|
||||||
|
where
|
||||||
|
push2ap (H.Push secret _ sharer repo mbranch mbefore after early mlate) = do
|
||||||
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
|
let shr = text2shr sharer
|
||||||
|
rp = text2rp repo
|
||||||
|
commit2ap' = commit2ap shr rp
|
||||||
|
(commitsLast, commitsFirst) <-
|
||||||
|
runDB $ case mlate of
|
||||||
|
Nothing -> (,) <$> traverse commit2ap' early <*> pure Nothing
|
||||||
|
Just (_omitted, late) ->
|
||||||
|
(,) <$> traverse commit2ap' late
|
||||||
|
<*> (Just <$> traverse commit2ap' early)
|
||||||
|
return
|
||||||
|
( Push
|
||||||
|
{ pushCommitsLast = commitsLast
|
||||||
|
, pushCommitsFirst = commitsFirst
|
||||||
|
, pushCommitsTotal =
|
||||||
|
case mlate of
|
||||||
|
Nothing -> length early
|
||||||
|
Just (omitted, late) ->
|
||||||
|
length early + omitted + length late
|
||||||
|
, pushTarget =
|
||||||
|
encodeRouteLocal $
|
||||||
|
case mbranch of
|
||||||
|
Nothing -> RepoR shr rp
|
||||||
|
Just b -> RepoBranchR shr rp b
|
||||||
|
, pushContext = encodeRouteLocal $ RepoR shr rp
|
||||||
|
, pushHashBefore = mbefore
|
||||||
|
, pushHashAfter = after
|
||||||
|
}
|
||||||
|
, shr
|
||||||
|
, rp
|
||||||
|
)
|
||||||
|
where
|
||||||
|
commit2ap shr rp (H.Commit (wauthor, wtime) mcommitted hash title desc) = do
|
||||||
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
|
encodeRouteHome <- getEncodeRouteHome
|
||||||
|
author <- authorByEmail wauthor
|
||||||
|
mcommitter <- traverse (authorByEmail . fst) mcommitted
|
||||||
|
return Commit
|
||||||
|
{ commitId = encodeRouteLocal $ RepoCommitR shr rp hash
|
||||||
|
, commitRepository = encodeRouteLocal $ RepoR shr rp
|
||||||
|
, commitAuthor = second (encodeRouteHome . SharerR) author
|
||||||
|
, commitCommitter =
|
||||||
|
second (encodeRouteHome . SharerR) <$> mcommitter
|
||||||
|
, commitTitle = title
|
||||||
|
, commitHash = Hash $ encodeUtf8 hash
|
||||||
|
, commitDescription =
|
||||||
|
if T.null desc
|
||||||
|
then Nothing
|
||||||
|
else Just desc
|
||||||
|
, commitWritten = wtime
|
||||||
|
, commitCommitted = snd <$> mcommitted
|
||||||
|
}
|
||||||
|
where
|
||||||
|
authorByEmail (H.Author name email) = do
|
||||||
|
mperson <- getValBy $ UniquePersonEmail email
|
||||||
|
case mperson of
|
||||||
|
Nothing -> return $ Left $ Author name email
|
||||||
|
Just person ->
|
||||||
|
Right . sharerIdent <$> getJust (personIdent person)
|
||||||
|
commitsText :: Maybe a -> Int -> Text
|
||||||
|
commitsText Nothing n =
|
||||||
|
if n > 1
|
||||||
|
then "patches"
|
||||||
|
else "patch"
|
||||||
|
commitsText (Just _) n =
|
||||||
|
if n > 1
|
||||||
|
then "commits"
|
||||||
|
else "commit"
|
||||||
|
--branchText :: ShrIdent -> RpIdent -> Maybe Text -> HtmlUrl (Route App)
|
||||||
|
branchText _ _ Nothing = const mempty
|
||||||
|
branchText shr rp (Just branch) =
|
||||||
|
[hamlet|
|
||||||
|
, branch #
|
||||||
|
<a href=@{RepoBranchR shr rp branch}>#{branch}
|
||||||
|
|]
|
||||||
|
-}
|
||||||
|
|
||||||
getRepoSourceR :: ShrIdent -> RpIdent -> [Text] -> Handler Html
|
|
||||||
getRepoSourceR shar repo refdir = do
|
|
||||||
repository <- runDB $ selectRepo shar repo
|
|
||||||
case repoVcs $ snd repository of
|
|
||||||
VCSDarcs -> getDarcsRepoSource repository shar repo refdir
|
|
||||||
VCSGit -> case refdir of
|
|
||||||
[] -> notFound
|
|
||||||
(ref:dir) -> getGitRepoSource repository shar repo ref dir
|
|
||||||
|
|
||||||
getRepoHeadChangesR :: ShrIdent -> RpIdent -> Handler TypedContent
|
|
||||||
getRepoHeadChangesR user repo = do
|
|
||||||
(_, repository) <- runDB $ selectRepo user repo
|
|
||||||
case repoVcs repository of
|
|
||||||
VCSDarcs -> getDarcsRepoHeadChanges user repo
|
|
||||||
VCSGit -> getGitRepoHeadChanges repository user repo
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{-
|
||||||
|
getReposR :: ShrIdent -> Handler Html
|
||||||
|
getReposR user = do
|
||||||
|
repos <- runDB $ E.select $ E.from $ \ (sharer, repo) -> do
|
||||||
|
E.where_ $
|
||||||
|
sharer E.^. SharerIdent E.==. E.val user E.&&.
|
||||||
|
sharer E.^. SharerId E.==. repo E.^. RepoSharer
|
||||||
|
E.orderBy [E.asc $ repo E.^. RepoIdent]
|
||||||
|
return $ repo E.^. RepoIdent
|
||||||
|
defaultLayout $(widgetFile "repo/list")
|
||||||
|
|
||||||
|
selectRepo :: ShrIdent -> RpIdent -> AppDB (Maybe (Sharer, Project, Workflow, Sharer), Repo)
|
||||||
|
selectRepo shar repo = do
|
||||||
|
Entity sid _s <- getBy404 $ UniqueSharer shar
|
||||||
|
Entity _rid r <- getBy404 $ UniqueRepo repo sid
|
||||||
|
mj <- for (repoProject r) $ \ jid -> do
|
||||||
|
j <- get404 jid
|
||||||
|
s <- get404 $ projectSharer j
|
||||||
|
w <- get404 $ projectWorkflow j
|
||||||
|
sw <- get404 $ workflowSharer w
|
||||||
|
return (s, j, w, sw)
|
||||||
|
return (mj, r)
|
||||||
|
|
||||||
getRepoBranchR :: ShrIdent -> RpIdent -> Text -> Handler TypedContent
|
getRepoBranchR :: ShrIdent -> RpIdent -> Text -> Handler TypedContent
|
||||||
getRepoBranchR shar repo ref = do
|
getRepoBranchR shar repo ref = do
|
||||||
|
@ -356,20 +683,6 @@ getRepoBranchR shar repo ref = do
|
||||||
VCSDarcs -> notFound
|
VCSDarcs -> notFound
|
||||||
VCSGit -> getGitRepoBranch shar repo ref
|
VCSGit -> getGitRepoBranch shar repo ref
|
||||||
|
|
||||||
getRepoChangesR :: ShrIdent -> RpIdent -> Text -> Handler TypedContent
|
|
||||||
getRepoChangesR shar repo ref = do
|
|
||||||
(_, repository) <- runDB $ selectRepo shar repo
|
|
||||||
case repoVcs repository of
|
|
||||||
VCSDarcs -> getDarcsRepoChanges shar repo ref
|
|
||||||
VCSGit -> getGitRepoChanges shar repo ref
|
|
||||||
|
|
||||||
getRepoCommitR :: ShrIdent -> RpIdent -> Text -> Handler TypedContent
|
|
||||||
getRepoCommitR shr rp ref = do
|
|
||||||
(_, repository) <- runDB $ selectRepo shr rp
|
|
||||||
case repoVcs repository of
|
|
||||||
VCSDarcs -> getDarcsPatch shr rp ref
|
|
||||||
VCSGit -> getGitPatch shr rp ref
|
|
||||||
|
|
||||||
getRepoDevsR :: ShrIdent -> RpIdent -> Handler Html
|
getRepoDevsR :: ShrIdent -> RpIdent -> Handler Html
|
||||||
getRepoDevsR shr rp = do
|
getRepoDevsR shr rp = do
|
||||||
devs <- runDB $ do
|
devs <- runDB $ do
|
||||||
|
@ -551,125 +864,4 @@ getHighlightStyleR styleName =
|
||||||
Nothing -> notFound
|
Nothing -> notFound
|
||||||
Just style ->
|
Just style ->
|
||||||
return $ TypedContent typeCss $ toContent $ styleToCss style
|
return $ TypedContent typeCss $ toContent $ styleToCss style
|
||||||
|
-}
|
||||||
postPostReceiveR :: Handler Text
|
|
||||||
postPostReceiveR = do
|
|
||||||
push <- requireCheckJsonBody
|
|
||||||
(pushAP, shr, rp) <- push2ap push
|
|
||||||
user <- runDB $ do
|
|
||||||
p <- getJustEntity $ toSqlKey $ H.pushUser push
|
|
||||||
s <- getJust $ personIdent $ entityVal p
|
|
||||||
return (p, s)
|
|
||||||
let shrUser = sharerIdent $ snd user
|
|
||||||
summary <- do
|
|
||||||
let mbranch = H.pushBranch push
|
|
||||||
total = pushCommitsTotal pushAP
|
|
||||||
lasts = pushCommitsLast pushAP
|
|
||||||
rest firsts = total - length firsts - length lasts
|
|
||||||
hashText (Hash b) = decodeUtf8 b
|
|
||||||
commitW c =
|
|
||||||
[hamlet|
|
|
||||||
<a href=@{RepoCommitR shr rp $ hashText $ commitHash c}>
|
|
||||||
#{commitTitle c}
|
|
||||||
|]
|
|
||||||
withUrlRenderer
|
|
||||||
[hamlet|
|
|
||||||
<p>
|
|
||||||
<a href=@{SharerR shrUser}>#{shr2text shrUser}
|
|
||||||
\ pushed #{total} #
|
|
||||||
\ #{commitsText mbranch total} to repo #
|
|
||||||
<a href=@{RepoR shr rp}>#{rp2text rp}</a>^{branchText shr rp mbranch}:
|
|
||||||
<ul>
|
|
||||||
$maybe firsts <- pushCommitsFirst pushAP
|
|
||||||
$forall c <- firsts
|
|
||||||
<li>^{commitW c}
|
|
||||||
<li>#{rest firsts}
|
|
||||||
$forall c <- lasts
|
|
||||||
<li>^{commitW c}
|
|
||||||
|]
|
|
||||||
eid <- runExceptT $ pushCommitsC user summary pushAP shr rp
|
|
||||||
case eid of
|
|
||||||
Left e -> liftIO $ throwIO $ userError $ T.unpack e
|
|
||||||
Right obiid -> do
|
|
||||||
renderUrl <- askUrlRender
|
|
||||||
obikhid <- encodeKeyHashid obiid
|
|
||||||
return $
|
|
||||||
"Push activity published: " <>
|
|
||||||
renderUrl (SharerOutboxItemR shrUser obikhid)
|
|
||||||
where
|
|
||||||
push2ap (H.Push secret _ sharer repo mbranch mbefore after early mlate) = do
|
|
||||||
encodeRouteLocal <- getEncodeRouteLocal
|
|
||||||
let shr = text2shr sharer
|
|
||||||
rp = text2rp repo
|
|
||||||
commit2ap' = commit2ap shr rp
|
|
||||||
(commitsLast, commitsFirst) <-
|
|
||||||
runDB $ case mlate of
|
|
||||||
Nothing -> (,) <$> traverse commit2ap' early <*> pure Nothing
|
|
||||||
Just (_omitted, late) ->
|
|
||||||
(,) <$> traverse commit2ap' late
|
|
||||||
<*> (Just <$> traverse commit2ap' early)
|
|
||||||
return
|
|
||||||
( Push
|
|
||||||
{ pushCommitsLast = commitsLast
|
|
||||||
, pushCommitsFirst = commitsFirst
|
|
||||||
, pushCommitsTotal =
|
|
||||||
case mlate of
|
|
||||||
Nothing -> length early
|
|
||||||
Just (omitted, late) ->
|
|
||||||
length early + omitted + length late
|
|
||||||
, pushTarget =
|
|
||||||
encodeRouteLocal $
|
|
||||||
case mbranch of
|
|
||||||
Nothing -> RepoR shr rp
|
|
||||||
Just b -> RepoBranchR shr rp b
|
|
||||||
, pushContext = encodeRouteLocal $ RepoR shr rp
|
|
||||||
, pushHashBefore = mbefore
|
|
||||||
, pushHashAfter = after
|
|
||||||
}
|
|
||||||
, shr
|
|
||||||
, rp
|
|
||||||
)
|
|
||||||
where
|
|
||||||
commit2ap shr rp (H.Commit (wauthor, wtime) mcommitted hash title desc) = do
|
|
||||||
encodeRouteLocal <- getEncodeRouteLocal
|
|
||||||
encodeRouteHome <- getEncodeRouteHome
|
|
||||||
author <- authorByEmail wauthor
|
|
||||||
mcommitter <- traverse (authorByEmail . fst) mcommitted
|
|
||||||
return Commit
|
|
||||||
{ commitId = encodeRouteLocal $ RepoCommitR shr rp hash
|
|
||||||
, commitRepository = encodeRouteLocal $ RepoR shr rp
|
|
||||||
, commitAuthor = second (encodeRouteHome . SharerR) author
|
|
||||||
, commitCommitter =
|
|
||||||
second (encodeRouteHome . SharerR) <$> mcommitter
|
|
||||||
, commitTitle = title
|
|
||||||
, commitHash = Hash $ encodeUtf8 hash
|
|
||||||
, commitDescription =
|
|
||||||
if T.null desc
|
|
||||||
then Nothing
|
|
||||||
else Just desc
|
|
||||||
, commitWritten = wtime
|
|
||||||
, commitCommitted = snd <$> mcommitted
|
|
||||||
}
|
|
||||||
where
|
|
||||||
authorByEmail (H.Author name email) = do
|
|
||||||
mperson <- getValBy $ UniquePersonEmail email
|
|
||||||
case mperson of
|
|
||||||
Nothing -> return $ Left $ Author name email
|
|
||||||
Just person ->
|
|
||||||
Right . sharerIdent <$> getJust (personIdent person)
|
|
||||||
commitsText :: Maybe a -> Int -> Text
|
|
||||||
commitsText Nothing n =
|
|
||||||
if n > 1
|
|
||||||
then "patches"
|
|
||||||
else "patch"
|
|
||||||
commitsText (Just _) n =
|
|
||||||
if n > 1
|
|
||||||
then "commits"
|
|
||||||
else "commit"
|
|
||||||
--branchText :: ShrIdent -> RpIdent -> Maybe Text -> HtmlUrl (Route App)
|
|
||||||
branchText _ _ Nothing = const mempty
|
|
||||||
branchText shr rp (Just branch) =
|
|
||||||
[hamlet|
|
|
||||||
, branch #
|
|
||||||
<a href=@{RepoBranchR shr rp branch}>#{branch}
|
|
||||||
|]
|
|
||||||
|
|
|
@ -17,7 +17,6 @@ module Vervis.Handler.Repo.Darcs
|
||||||
( getDarcsRepoSource
|
( getDarcsRepoSource
|
||||||
, getDarcsRepoHeadChanges
|
, getDarcsRepoHeadChanges
|
||||||
, getDarcsRepoChanges
|
, getDarcsRepoChanges
|
||||||
, getDarcsDownloadR
|
|
||||||
, getDarcsPatch
|
, getDarcsPatch
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
@ -61,7 +60,6 @@ import Text.FilePath.Local (breakExt)
|
||||||
import Vervis.ActivityPub
|
import Vervis.ActivityPub
|
||||||
import Vervis.ChangeFeed (changeFeed)
|
import Vervis.ChangeFeed (changeFeed)
|
||||||
import Vervis.Changes
|
import Vervis.Changes
|
||||||
import Vervis.Form.Repo
|
|
||||||
import Vervis.Foundation
|
import Vervis.Foundation
|
||||||
import Vervis.Path
|
import Vervis.Path
|
||||||
import Vervis.Model
|
import Vervis.Model
|
||||||
|
@ -73,10 +71,6 @@ import Vervis.Settings
|
||||||
import Vervis.SourceTree
|
import Vervis.SourceTree
|
||||||
import Vervis.Style
|
import Vervis.Style
|
||||||
import Vervis.Time
|
import Vervis.Time
|
||||||
import Vervis.Widget (buttonW)
|
|
||||||
import Vervis.Widget.Project
|
|
||||||
import Vervis.Widget.Repo
|
|
||||||
import Vervis.Widget.Sharer
|
|
||||||
|
|
||||||
import qualified Vervis.Darcs as D (readSourceView, readChangesView, readPatch)
|
import qualified Vervis.Darcs as D (readSourceView, readChangesView, readPatch)
|
||||||
|
|
||||||
|
@ -163,16 +157,6 @@ getDarcsRepoHeadChanges shar repo = do
|
||||||
getDarcsRepoChanges :: ShrIdent -> RpIdent -> Text -> Handler TypedContent
|
getDarcsRepoChanges :: ShrIdent -> RpIdent -> Text -> Handler TypedContent
|
||||||
getDarcsRepoChanges shar repo tag = notFound
|
getDarcsRepoChanges shar repo tag = notFound
|
||||||
|
|
||||||
getDarcsDownloadR :: ShrIdent -> RpIdent -> [Text] -> Handler TypedContent
|
|
||||||
getDarcsDownloadR shar repo dir = do
|
|
||||||
path <- askRepoDir shar repo
|
|
||||||
let darcsDir = path </> "_darcs"
|
|
||||||
filePath = darcsDir </> joinPath (map T.unpack dir)
|
|
||||||
exists <- liftIO $ doesFileExist filePath
|
|
||||||
if exists
|
|
||||||
then sendFile typeOctet filePath
|
|
||||||
else notFound
|
|
||||||
|
|
||||||
getDarcsPatch :: ShrIdent -> RpIdent -> Text -> Handler TypedContent
|
getDarcsPatch :: ShrIdent -> RpIdent -> Text -> Handler TypedContent
|
||||||
getDarcsPatch shr rp ref = do
|
getDarcsPatch shr rp ref = do
|
||||||
path <- askRepoDir shr rp
|
path <- askRepoDir shr rp
|
||||||
|
|
|
@ -75,7 +75,6 @@ import Text.FilePath.Local (breakExt)
|
||||||
import Vervis.ActivityPub
|
import Vervis.ActivityPub
|
||||||
import Vervis.ChangeFeed (changeFeed)
|
import Vervis.ChangeFeed (changeFeed)
|
||||||
import Vervis.Changes
|
import Vervis.Changes
|
||||||
import Vervis.Form.Repo
|
|
||||||
import Vervis.Foundation
|
import Vervis.Foundation
|
||||||
import Vervis.Path
|
import Vervis.Path
|
||||||
import Vervis.Model
|
import Vervis.Model
|
||||||
|
@ -87,10 +86,6 @@ import Vervis.Settings
|
||||||
import Vervis.SourceTree
|
import Vervis.SourceTree
|
||||||
import Vervis.Style
|
import Vervis.Style
|
||||||
import Vervis.Time (showDate)
|
import Vervis.Time (showDate)
|
||||||
import Vervis.Widget (buttonW)
|
|
||||||
import Vervis.Widget.Project
|
|
||||||
import Vervis.Widget.Repo
|
|
||||||
import Vervis.Widget.Sharer
|
|
||||||
|
|
||||||
import qualified Data.ByteString.Lazy as BL (ByteString)
|
import qualified Data.ByteString.Lazy as BL (ByteString)
|
||||||
import qualified Vervis.Git as G (readSourceView, readChangesView, listRefs, readPatch)
|
import qualified Vervis.Git as G (readSourceView, readChangesView, listRefs, readPatch)
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
|
|
||||||
module Vervis.Handler.Sharer
|
module Vervis.Handler.Sharer
|
||||||
( getSharersR
|
( getSharersR
|
||||||
, getSharerR
|
|
||||||
, getSharerFollowersR
|
, getSharerFollowersR
|
||||||
, getSharerFollowingR
|
, getSharerFollowingR
|
||||||
)
|
)
|
||||||
|
@ -65,22 +64,6 @@ getSharersR = do
|
||||||
let pageNav = navWidget navModel
|
let pageNav = navWidget navModel
|
||||||
defaultLayout $(widgetFile "sharer/list")
|
defaultLayout $(widgetFile "sharer/list")
|
||||||
|
|
||||||
getSharerR :: ShrIdent -> Handler TypedContent
|
|
||||||
getSharerR shr = do
|
|
||||||
ment <- runDB $ do
|
|
||||||
Entity sid sharer <- getBy404 $ UniqueSharer shr
|
|
||||||
runMaybeT . fmap (sharer,)
|
|
||||||
$ Left <$> MaybeT (getBy $ UniquePersonIdent sid)
|
|
||||||
<|> Right <$> MaybeT (getBy $ UniqueGroup sid)
|
|
||||||
case ment of
|
|
||||||
Nothing -> do
|
|
||||||
$logWarn $ "Found non-person non-group sharer: " <> shr2text shr
|
|
||||||
notFound
|
|
||||||
Just (s, ent) ->
|
|
||||||
case ent of
|
|
||||||
Left ep -> getPerson shr s ep
|
|
||||||
Right (Entity _ g) -> getGroup shr g
|
|
||||||
|
|
||||||
getSharerFollowersR :: ShrIdent -> Handler TypedContent
|
getSharerFollowersR :: ShrIdent -> Handler TypedContent
|
||||||
getSharerFollowersR shr = getFollowersCollection here getFsid
|
getSharerFollowersR shr = getFollowersCollection here getFsid
|
||||||
where
|
where
|
||||||
|
@ -98,84 +81,3 @@ getSharerFollowersR shr = getFollowersCollection here getFsid
|
||||||
case val of
|
case val of
|
||||||
Left person -> return $ personFollowers person
|
Left person -> return $ personFollowers person
|
||||||
Right _group -> notFound
|
Right _group -> notFound
|
||||||
|
|
||||||
getSharerFollowingR :: ShrIdent -> Handler TypedContent
|
|
||||||
getSharerFollowingR shr = do
|
|
||||||
(localTotal, sharers, projects, tickets, repos, remotes) <- runDB $ do
|
|
||||||
sid <- getKeyBy404 $ UniqueSharer shr
|
|
||||||
pid <- getKeyBy404 $ UniquePersonIdent sid
|
|
||||||
fsids <-
|
|
||||||
map (followTarget . entityVal) <$>
|
|
||||||
selectList [FollowPerson ==. pid] []
|
|
||||||
(,,,,,) (length fsids)
|
|
||||||
<$> getSharers fsids
|
|
||||||
<*> getProjects fsids
|
|
||||||
<*> getTickets fsids
|
|
||||||
<*> getRepos fsids
|
|
||||||
<*> getRemotes pid
|
|
||||||
let locals = sharers ++ projects ++ tickets ++ repos
|
|
||||||
unless (length locals == localTotal) $
|
|
||||||
liftIO $ throwIO $ userError "Bug! List length mismatch"
|
|
||||||
|
|
||||||
encodeRouteLocal <- getEncodeRouteLocal
|
|
||||||
encodeRouteHome <- getEncodeRouteHome
|
|
||||||
let here = SharerFollowingR shr
|
|
||||||
followingAP = Collection
|
|
||||||
{ collectionId = encodeRouteLocal here
|
|
||||||
, collectionType = CollectionTypeUnordered
|
|
||||||
, collectionTotalItems = Just $ localTotal + length remotes
|
|
||||||
, collectionCurrent = Nothing
|
|
||||||
, collectionFirst = Nothing
|
|
||||||
, collectionLast = Nothing
|
|
||||||
, collectionItems = map encodeRouteHome locals ++ remotes
|
|
||||||
}
|
|
||||||
provideHtmlAndAP followingAP $ redirectToPrettyJSON here
|
|
||||||
where
|
|
||||||
getSharers fsids = do
|
|
||||||
sids <-
|
|
||||||
map (personIdent . entityVal) <$>
|
|
||||||
selectList [PersonFollowers <-. fsids] []
|
|
||||||
map (SharerR . sharerIdent . entityVal) <$>
|
|
||||||
selectList [SharerId <-. sids] []
|
|
||||||
getProjects fsids =
|
|
||||||
fmap (map $ \ (E.Value shr, E.Value prj) -> ProjectR shr prj) $
|
|
||||||
E.select $ E.from $ \ (a `E.InnerJoin` j `E.InnerJoin` s) -> do
|
|
||||||
E.on $ j E.^. ProjectSharer E.==. s E.^. SharerId
|
|
||||||
E.on $ a E.^. ActorId E.==. j E.^. ProjectActor
|
|
||||||
E.where_ $ a E.^. ActorFollowers `E.in_` E.valList fsids
|
|
||||||
return (s E.^. SharerIdent, j E.^. ProjectIdent)
|
|
||||||
getTickets fsids = do
|
|
||||||
ltids <- selectKeysList [LocalTicketFollowers <-. fsids] []
|
|
||||||
triples <-
|
|
||||||
E.select $ E.from $
|
|
||||||
\ (lt `E.InnerJoin`
|
|
||||||
t `E.InnerJoin`
|
|
||||||
tcl `E.InnerJoin`
|
|
||||||
tpl `E.InnerJoin`
|
|
||||||
j `E.InnerJoin`
|
|
||||||
s) -> do
|
|
||||||
E.on $ j E.^. ProjectSharer E.==. s E.^. SharerId
|
|
||||||
E.on $ tpl E.^. TicketProjectLocalProject E.==. j E.^. ProjectId
|
|
||||||
E.on $ tcl E.^. TicketContextLocalId E.==. tpl E.^. TicketProjectLocalContext
|
|
||||||
E.on $ t E.^. TicketId E.==. tcl E.^. TicketContextLocalTicket
|
|
||||||
E.on $ lt E.^. LocalTicketTicket E.==. t E.^. TicketId
|
|
||||||
E.where_ $ lt E.^. LocalTicketId `E.in_` E.valList ltids
|
|
||||||
return
|
|
||||||
( s E.^. SharerIdent
|
|
||||||
, j E.^. ProjectIdent
|
|
||||||
, lt E.^. LocalTicketId
|
|
||||||
)
|
|
||||||
encodeHid <- getEncodeKeyHashid
|
|
||||||
return $
|
|
||||||
map (\ (E.Value shr, E.Value prj, E.Value tid) -> ProjectTicketR shr prj $ encodeHid tid)
|
|
||||||
triples
|
|
||||||
getRepos fsids = do
|
|
||||||
rids <- selectKeysList [RepoFollowers <-. fsids] []
|
|
||||||
pairs <- E.select $ E.from $ \ (r `E.InnerJoin` s) -> do
|
|
||||||
E.on $ r E.^. RepoSharer E.==. s E.^. SharerId
|
|
||||||
E.where_ $ r E.^. RepoId `E.in_` E.valList rids
|
|
||||||
return (s E.^. SharerIdent, r E.^. RepoIdent)
|
|
||||||
return $ map (\ (E.Value shr, E.Value rp) -> RepoR shr rp) pairs
|
|
||||||
getRemotes pid =
|
|
||||||
map (followRemoteTarget . entityVal) <$>
|
|
||||||
selectList [FollowRemotePerson ==. pid] []
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
{- This file is part of Vervis.
|
{- This file is part of Vervis.
|
||||||
-
|
-
|
||||||
- Written in 2016, 2018, 2019, 2020 by fr33domlover <fr33domlover@riseup.net>.
|
- Written in 2016, 2018, 2019, 2020, 2022
|
||||||
|
- 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.
|
||||||
-
|
-
|
||||||
|
@ -14,10 +15,25 @@
|
||||||
-}
|
-}
|
||||||
|
|
||||||
module Vervis.Handler.Ticket
|
module Vervis.Handler.Ticket
|
||||||
( getProjectTicketsR
|
( getTicketR
|
||||||
|
, getTicketDiscussionR
|
||||||
|
, getTicketEventsR
|
||||||
|
, getTicketFollowersR
|
||||||
|
, getTicketDepsR
|
||||||
|
, getTicketReverseDepsR
|
||||||
|
|
||||||
|
, getTicketDepR
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{-
|
||||||
|
, getProjectTicketsR
|
||||||
, getProjectTicketTreeR
|
, getProjectTicketTreeR
|
||||||
, getProjectTicketNewR
|
, getProjectTicketNewR
|
||||||
, getProjectTicketR
|
|
||||||
, putProjectTicketR
|
, putProjectTicketR
|
||||||
, deleteProjectTicketR
|
, deleteProjectTicketR
|
||||||
, postProjectTicketR
|
, postProjectTicketR
|
||||||
|
@ -33,31 +49,26 @@ module Vervis.Handler.Ticket
|
||||||
, getClaimRequestsTicketR
|
, getClaimRequestsTicketR
|
||||||
, postClaimRequestsTicketR
|
, postClaimRequestsTicketR
|
||||||
, getClaimRequestNewR
|
, getClaimRequestNewR
|
||||||
, getProjectTicketDiscussionR
|
|
||||||
, postProjectTicketDiscussionR
|
, postProjectTicketDiscussionR
|
||||||
, getMessageR
|
, getMessageR
|
||||||
, postProjectTicketMessageR
|
, postProjectTicketMessageR
|
||||||
, getProjectTicketTopReplyR
|
, getProjectTicketTopReplyR
|
||||||
, getProjectTicketReplyR
|
, getProjectTicketReplyR
|
||||||
, getProjectTicketDepsR
|
|
||||||
, postProjectTicketDepsR
|
, postProjectTicketDepsR
|
||||||
, getProjectTicketDepNewR
|
, getProjectTicketDepNewR
|
||||||
, postTicketDepOldR
|
, postTicketDepOldR
|
||||||
, deleteTicketDepOldR
|
, deleteTicketDepOldR
|
||||||
, getProjectTicketReverseDepsR
|
|
||||||
, getTicketDepR
|
|
||||||
, getProjectTicketParticipantsR
|
, getProjectTicketParticipantsR
|
||||||
, getProjectTicketTeamR
|
, getProjectTicketTeamR
|
||||||
, getProjectTicketEventsR
|
|
||||||
|
|
||||||
, getSharerTicketsR
|
, getSharerTicketsR
|
||||||
, getSharerTicketR
|
, getSharerTicketR
|
||||||
, getSharerTicketDiscussionR
|
, getSharerTicketDiscussionR
|
||||||
, getSharerTicketDepsR
|
, getSharerTicketDepsR
|
||||||
, getSharerTicketReverseDepsR
|
, getSharerTicketReverseDepsR
|
||||||
, getSharerTicketFollowersR
|
|
||||||
, getSharerTicketTeamR
|
, getSharerTicketTeamR
|
||||||
, getSharerTicketEventsR
|
, getSharerTicketEventsR
|
||||||
|
-}
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
|
@ -120,11 +131,11 @@ import Database.Persist.Local
|
||||||
import Yesod.Persist.Local
|
import Yesod.Persist.Local
|
||||||
|
|
||||||
import Vervis.ActivityPub
|
import Vervis.ActivityPub
|
||||||
|
import Vervis.Actor
|
||||||
import Vervis.API
|
import Vervis.API
|
||||||
import Vervis.Discussion
|
import Vervis.Discussion
|
||||||
import Vervis.Federation
|
import Vervis.Federation
|
||||||
import Vervis.FedURI
|
import Vervis.FedURI
|
||||||
import Vervis.Form.Ticket
|
|
||||||
import Vervis.Foundation
|
import Vervis.Foundation
|
||||||
import Vervis.Handler.Discussion
|
import Vervis.Handler.Discussion
|
||||||
--import Vervis.GraphProxy (ticketDepGraph)
|
--import Vervis.GraphProxy (ticketDepGraph)
|
||||||
|
@ -138,211 +149,101 @@ import Vervis.Style
|
||||||
import Vervis.Ticket
|
import Vervis.Ticket
|
||||||
import Vervis.TicketFilter (filterTickets)
|
import Vervis.TicketFilter (filterTickets)
|
||||||
import Vervis.Time (showDate)
|
import Vervis.Time (showDate)
|
||||||
import Vervis.Widget (buttonW)
|
|
||||||
import Vervis.Widget.Discussion (discussionW)
|
|
||||||
import Vervis.Widget.Sharer
|
|
||||||
import Vervis.Widget.Ticket
|
|
||||||
|
|
||||||
getProjectTicketsR :: ShrIdent -> PrjIdent -> Handler TypedContent
|
getTicketR :: KeyHashid Deck -> KeyHashid TicketDeck -> Handler TypedContent
|
||||||
getProjectTicketsR shr prj = selectRep $ do
|
getTicketR deckHash ticketHash = do
|
||||||
provideRep $ do
|
(ticket, author, resolve) <- runDB $ do
|
||||||
((filtResult, filtWidget), filtEnctype) <- runFormGet ticketFilterForm
|
(_, _, Entity _ ticket', author', resolve') <-
|
||||||
let tf =
|
getTicket404 deckHash ticketHash
|
||||||
case filtResult of
|
(,,) ticket'
|
||||||
FormSuccess filt -> filt
|
<$> (case author' of
|
||||||
FormMissing -> def
|
Left (Entity _ tal) ->
|
||||||
FormFailure l ->
|
return $ Left $ ticketAuthorLocalAuthor tal
|
||||||
error $ "Ticket filter form failed: " ++ show l
|
Right (Entity _ tar) -> Right <$> do
|
||||||
(total, pages, mpage) <- runDB $ do
|
ra <- getJust $ ticketAuthorRemoteAuthor tar
|
||||||
Entity sid _ <- getBy404 $ UniqueSharer shr
|
ro <- getJust $ remoteActorIdent ra
|
||||||
Entity jid _ <- getBy404 $ UniqueProject prj sid
|
i <- getJust $ remoteObjectInstance ro
|
||||||
let countAllTickets = count [TicketProjectLocalProject ==. jid]
|
return (i, ro)
|
||||||
selectTickets off lim =
|
)
|
||||||
getTicketSummaries
|
<*> (for resolve' $ \ (_, etrx) ->
|
||||||
(filterTickets tf)
|
bitraverse
|
||||||
(Just $ \ t -> [E.asc $ t E.^. TicketId])
|
(\ (Entity _ trl) -> do
|
||||||
(Just (off, lim))
|
let obiid = ticketResolveLocalActivity trl
|
||||||
jid
|
obid <- outboxItemOutbox <$> getJust obiid
|
||||||
getPageAndNavCount countAllTickets selectTickets
|
actorID <- do
|
||||||
case mpage of
|
maybeActorID <- getKeyBy $ UniqueActorOutbox obid
|
||||||
Nothing -> redirectFirstPage here
|
case maybeActorID of
|
||||||
Just (rows, navModel) ->
|
Nothing -> error "Found outbox not used by any actor"
|
||||||
let pageNav = navWidget navModel
|
Just a -> return a
|
||||||
in defaultLayout $(widgetFile "ticket/list")
|
actor <- getLocalActor actorID
|
||||||
provideAP' $ do
|
return (actor, obiid)
|
||||||
(total, pages, mpage) <- runDB $ do
|
)
|
||||||
Entity sid _ <- getBy404 $ UniqueSharer shr
|
(\ (Entity _ trr) -> do
|
||||||
Entity jid _ <- getBy404 $ UniqueProject prj sid
|
roid <-
|
||||||
let countAllTickets = count [TicketProjectLocalProject ==. jid]
|
remoteActivityIdent <$>
|
||||||
selectTickets off lim = do
|
getJust (ticketResolveRemoteActivity trr)
|
||||||
tids <- E.select $ E.from $ \ (tcl `E.InnerJoin` tpl) -> do
|
ro <- getJust roid
|
||||||
E.on $ tcl E.^. TicketContextLocalId E.==. tpl E.^. TicketProjectLocalContext
|
i <- getJust $ remoteObjectInstance ro
|
||||||
E.where_ $ tpl E.^. TicketProjectLocalProject E.==. E.val jid
|
return (i, ro)
|
||||||
E.orderBy [E.desc $ tcl E.^. TicketContextLocalTicket]
|
)
|
||||||
E.offset $ fromIntegral off
|
etrx
|
||||||
E.limit $ fromIntegral lim
|
)
|
||||||
return $ tcl E.^. TicketContextLocalTicket
|
|
||||||
let tids' = map E.unValue tids
|
|
||||||
locals <- E.select $ E.from $ \ (lt `E.LeftOuterJoin` (tal `E.InnerJoin` p `E.InnerJoin` s `E.LeftOuterJoin` tup)) -> do
|
|
||||||
E.on $ tal E.?. TicketAuthorLocalId E.==. tup E.?. TicketUnderProjectAuthor
|
|
||||||
E.on $ p E.?. PersonIdent E.==. s E.?. SharerId
|
|
||||||
E.on $ tal E.?. TicketAuthorLocalAuthor E.==. p E.?. PersonId
|
|
||||||
E.on $ E.just (lt E.^. LocalTicketId) E.==. tal E.?. TicketAuthorLocalTicket
|
|
||||||
E.where_ $ lt E.^. LocalTicketTicket `E.in_` E.valList tids'
|
|
||||||
E.orderBy [E.desc $ lt E.^. LocalTicketTicket]
|
|
||||||
return
|
|
||||||
( lt E.^. LocalTicketTicket
|
|
||||||
, ( lt E.^. LocalTicketId
|
|
||||||
, tal E.?. TicketAuthorLocalId
|
|
||||||
, s E.?. SharerIdent
|
|
||||||
, tup E.?. TicketUnderProjectId
|
|
||||||
)
|
|
||||||
)
|
|
||||||
remotes <- E.select $ E.from $ \ (tcl `E.InnerJoin` tar `E.InnerJoin` rt `E.InnerJoin` ro `E.InnerJoin` i) -> do
|
|
||||||
E.on $ ro E.^. RemoteObjectInstance E.==. i E.^. InstanceId
|
|
||||||
E.on $ rt E.^. RemoteTicketIdent E.==. ro E.^. RemoteObjectId
|
|
||||||
E.on $ tar E.^. TicketAuthorRemoteId E.==. rt E.^. RemoteTicketTicket
|
|
||||||
E.on $ tcl E.^. TicketContextLocalId E.==. tar E.^. TicketAuthorRemoteTicket
|
|
||||||
E.where_ $ tcl E.^. TicketContextLocalTicket `E.in_` E.valList tids'
|
|
||||||
E.orderBy [E.desc $ tcl E.^. TicketContextLocalTicket]
|
|
||||||
return
|
|
||||||
( tcl E.^. TicketContextLocalTicket
|
|
||||||
, ( i E.^. InstanceHost
|
|
||||||
, ro E.^. RemoteObjectIdent
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return $
|
|
||||||
map snd $
|
|
||||||
LO.mergeBy
|
|
||||||
(flip compare `on` fst)
|
|
||||||
(map (second Left) locals)
|
|
||||||
(map (second Right) remotes)
|
|
||||||
getPageAndNavCount countAllTickets selectTickets
|
|
||||||
|
|
||||||
encodeRouteHome <- getEncodeRouteHome
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
encodeRouteLocal <- getEncodeRouteLocal
|
encodeRouteHome <- getEncodeRouteHome
|
||||||
encodeRoutePageLocal <- getEncodeRoutePageLocal
|
hashPerson <- getEncodeKeyHashid
|
||||||
let pageUrl = encodeRoutePageLocal here
|
hashItem <- getEncodeKeyHashid
|
||||||
host <- asksSite siteInstanceHost
|
hLocal <- getsYesod siteInstanceHost
|
||||||
encodeLT <- getEncodeKeyHashid
|
let route mk = encodeRouteLocal $ mk deckHash ticketHash
|
||||||
encodeTAL <- getEncodeKeyHashid
|
authorHost =
|
||||||
|
case author of
|
||||||
|
Left _ -> hLocal
|
||||||
|
Right (i, _) -> instanceHost i
|
||||||
|
ticketLocalAP = AP.TicketLocal
|
||||||
|
{ AP.ticketId = route TicketR
|
||||||
|
, AP.ticketReplies = route TicketDiscussionR
|
||||||
|
, AP.ticketParticipants = route TicketFollowersR
|
||||||
|
, AP.ticketTeam = Nothing
|
||||||
|
, AP.ticketEvents = route TicketEventsR
|
||||||
|
, AP.ticketDeps = route TicketDepsR
|
||||||
|
, AP.ticketReverseDeps = route TicketReverseDepsR
|
||||||
|
}
|
||||||
|
ticketAP = AP.Ticket
|
||||||
|
{ AP.ticketLocal = Just (hLocal, ticketLocalAP)
|
||||||
|
, AP.ticketAttributedTo =
|
||||||
|
case author of
|
||||||
|
Left authorID ->
|
||||||
|
encodeRouteLocal $ PersonR $ hashPerson authorID
|
||||||
|
Right (_instance, object) ->
|
||||||
|
remoteObjectIdent object
|
||||||
|
, AP.ticketPublished = Just $ ticketCreated ticket
|
||||||
|
, AP.ticketUpdated = Nothing
|
||||||
|
, AP.ticketContext = Just $ encodeRouteHome $ DeckR deckHash
|
||||||
|
-- , AP.ticketName = Just $ "#" <> T.pack (show num)
|
||||||
|
, AP.ticketSummary = TextHtml $ ticketTitle ticket
|
||||||
|
, AP.ticketContent = TextHtml $ ticketDescription ticket
|
||||||
|
, AP.ticketSource = TextPandocMarkdown $ ticketSource ticket
|
||||||
|
, AP.ticketAssignedTo = Nothing
|
||||||
|
, AP.ticketResolved =
|
||||||
|
let u (Left (actor, obiid)) =
|
||||||
|
encodeRouteHome $
|
||||||
|
outboxItemRoute actor $ hashItem obiid
|
||||||
|
u (Right (i, ro)) =
|
||||||
|
ObjURI (instanceHost i) (remoteObjectIdent ro)
|
||||||
|
in (,Nothing) . Just . u <$> resolve
|
||||||
|
, AP.ticketAttachment = Nothing
|
||||||
|
}
|
||||||
|
|
||||||
return $
|
provideHtmlAndAP' authorHost ticketAP $ redirectToPrettyJSON here
|
||||||
case mpage of
|
|
||||||
Nothing -> encodeStrict $ Doc host $ Collection
|
|
||||||
{ collectionId = encodeRouteLocal here
|
|
||||||
, collectionType = CollectionTypeOrdered
|
|
||||||
, collectionTotalItems = Just total
|
|
||||||
, collectionCurrent = Nothing
|
|
||||||
, collectionFirst = Just $ pageUrl 1
|
|
||||||
, collectionLast = Just $ pageUrl pages
|
|
||||||
, collectionItems = [] :: [Text]
|
|
||||||
}
|
|
||||||
Just (tickets, navModel) ->
|
|
||||||
let current = nmCurrent navModel
|
|
||||||
in encodeStrict $ Doc host $ 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 (ticketRoute encodeRouteHome encodeLT encodeTAL)
|
|
||||||
tickets
|
|
||||||
}
|
|
||||||
where
|
where
|
||||||
here = ProjectTicketsR shr prj
|
here = TicketR deckHash ticketHash
|
||||||
encodeStrict = BL.toStrict . encode
|
|
||||||
ticketRoute encodeRoute encodeLT encodeTAL (Left (E.Value ltid, E.Value mtalid, E.Value mshr, E.Value mtupid)) =
|
|
||||||
encodeRoute $
|
|
||||||
case (mtalid, mshr, mtupid) of
|
|
||||||
(Nothing, Nothing, Nothing) -> ProjectTicketR shr prj $ encodeLT ltid
|
|
||||||
(Just talid, Just shrA, Nothing) -> SharerTicketR shrA $ encodeTAL talid
|
|
||||||
(Just _, Just _, Just _) -> ProjectTicketR shr prj $ encodeLT ltid
|
|
||||||
_ -> error "Impossible"
|
|
||||||
ticketRoute _ _ _ (Right (E.Value h, E.Value lu)) = ObjURI h lu
|
|
||||||
|
|
||||||
getProjectTicketTreeR :: ShrIdent -> PrjIdent -> Handler Html
|
|
||||||
getProjectTicketTreeR _shr _prj = error "Ticket tree view disabled for now"
|
|
||||||
{-
|
{-
|
||||||
(summaries, deps) <- runDB $ do
|
|
||||||
Entity sid _ <- getBy404 $ UniqueSharer shr
|
|
||||||
Entity jid _ <- getBy404 $ UniqueProject prj sid
|
|
||||||
(,) <$> getTicketSummaries Nothing Nothing Nothing jid
|
|
||||||
<*> getTicketDepEdges jid
|
|
||||||
defaultLayout $ ticketTreeDW shr prj summaries deps
|
|
||||||
-}
|
|
||||||
|
|
||||||
getProjectTicketNewR :: ShrIdent -> PrjIdent -> Handler Html
|
|
||||||
getProjectTicketNewR shr prj = do
|
|
||||||
wid <- runDB $ do
|
|
||||||
Entity sid _ <- getBy404 $ UniqueSharer shr
|
|
||||||
Entity _ j <- getBy404 $ UniqueProject prj sid
|
|
||||||
return $ projectWorkflow j
|
|
||||||
((_result, widget), enctype) <- runFormPost $ newTicketForm wid
|
|
||||||
defaultLayout $(widgetFile "ticket/new")
|
|
||||||
|
|
||||||
getProjectTicketR :: ShrIdent -> PrjIdent -> KeyHashid LocalTicket -> Handler TypedContent
|
|
||||||
getProjectTicketR shar proj ltkhid = do
|
|
||||||
mpid <- maybeAuthId
|
mpid <- maybeAuthId
|
||||||
( wshr, wfl,
|
( wshr, wfl,
|
||||||
author, massignee, mresolved, ticket, lticket, tparams, eparams, cparams) <-
|
author, massignee, mresolved, ticket, lticket, tparams, eparams, cparams) <-
|
||||||
runDB $ do
|
runDB $ do
|
||||||
(Entity sid sharer, Entity jid project, Entity tid ticket, Entity _ lticket, _etcl, _etpl, author, resolved) <- getProjectTicket404 shar proj ltkhid
|
(Entity sid sharer, Entity jid project, Entity tid ticket, Entity _ lticket, _etcl, _etpl, author, resolved) <- getProjectTicket404 shar proj ltkhid
|
||||||
(wshr, wid, wfl) <- do
|
|
||||||
w <- get404 $ projectWorkflow project
|
|
||||||
wsharer <-
|
|
||||||
if workflowSharer w == sid
|
|
||||||
then return sharer
|
|
||||||
else get404 $ workflowSharer w
|
|
||||||
return
|
|
||||||
( sharerIdent wsharer
|
|
||||||
, projectWorkflow project
|
|
||||||
, workflowIdent w
|
|
||||||
)
|
|
||||||
author' <-
|
|
||||||
case author of
|
|
||||||
Left (Entity _ tal, _) -> Left <$> do
|
|
||||||
p <- getJust $ ticketAuthorLocalAuthor tal
|
|
||||||
getJust $ personIdent p
|
|
||||||
Right (Entity _ tar) -> Right <$> do
|
|
||||||
ra <- getJust $ ticketAuthorRemoteAuthor tar
|
|
||||||
ro <- getJust $ remoteActorIdent ra
|
|
||||||
i <- getJust $ remoteObjectInstance ro
|
|
||||||
return (i, ro, ra)
|
|
||||||
massignee <- for (ticketAssignee ticket) $ \ apid -> do
|
|
||||||
person <- get404 apid
|
|
||||||
sharer <- get404 $ personIdent person
|
|
||||||
return (sharer, fromMaybe False $ (== apid) <$> mpid)
|
|
||||||
mresolved <- for resolved $ \ (_, etrx) ->
|
|
||||||
bitraverse
|
|
||||||
(\ (Entity _ trl) -> do
|
|
||||||
let obiid = ticketResolveLocalActivity trl
|
|
||||||
obid <- outboxItemOutbox <$> getJust obiid
|
|
||||||
ent <- getOutboxActorEntity obid
|
|
||||||
actor <- actorEntityPath ent
|
|
||||||
return (actor, obiid)
|
|
||||||
)
|
|
||||||
(\ (Entity _ trr) -> do
|
|
||||||
roid <-
|
|
||||||
remoteActivityIdent <$>
|
|
||||||
getJust (ticketResolveRemoteActivity trr)
|
|
||||||
ro <- getJust roid
|
|
||||||
i <- getJust $ remoteObjectInstance ro
|
|
||||||
return (i, ro)
|
|
||||||
)
|
|
||||||
etrx
|
|
||||||
tparams <- getTicketTextParams tid wid
|
tparams <- getTicketTextParams tid wid
|
||||||
eparams <- getTicketEnumParams tid wid
|
eparams <- getTicketEnumParams tid wid
|
||||||
cparams <- getTicketClasses tid wid
|
cparams <- getTicketClasses tid wid
|
||||||
|
@ -351,7 +252,6 @@ getProjectTicketR shar proj ltkhid = do
|
||||||
, author', massignee, mresolved, ticket, lticket
|
, author', massignee, mresolved, ticket, lticket
|
||||||
, tparams, eparams, cparams
|
, tparams, eparams, cparams
|
||||||
)
|
)
|
||||||
encodeHid <- getEncodeKeyHashid
|
|
||||||
let desc :: Widget
|
let desc :: Widget
|
||||||
desc = toWidget $ preEscapedToMarkup $ ticketDescription ticket
|
desc = toWidget $ preEscapedToMarkup $ ticketDescription ticket
|
||||||
discuss =
|
discuss =
|
||||||
|
@ -367,60 +267,6 @@ getProjectTicketR shar proj ltkhid = do
|
||||||
TSNew -> wffNew filt
|
TSNew -> wffNew filt
|
||||||
TSTodo -> wffTodo filt
|
TSTodo -> wffTodo filt
|
||||||
TSClosed -> wffClosed filt
|
TSClosed -> wffClosed filt
|
||||||
hLocal <- getsYesod siteInstanceHost
|
|
||||||
encodeRouteLocal <- getEncodeRouteLocal
|
|
||||||
encodeRouteHome <- getEncodeRouteHome
|
|
||||||
encodeKeyHashid <- getEncodeKeyHashid
|
|
||||||
let host =
|
|
||||||
case author of
|
|
||||||
Left _ -> hLocal
|
|
||||||
Right (i, _, _) -> instanceHost i
|
|
||||||
ticketAP = AP.Ticket
|
|
||||||
{ AP.ticketLocal = Just
|
|
||||||
( hLocal
|
|
||||||
, AP.TicketLocal
|
|
||||||
{ AP.ticketId =
|
|
||||||
encodeRouteLocal $ ProjectTicketR shar proj ltkhid
|
|
||||||
, AP.ticketReplies =
|
|
||||||
encodeRouteLocal $ ProjectTicketDiscussionR shar proj ltkhid
|
|
||||||
, AP.ticketParticipants =
|
|
||||||
encodeRouteLocal $ ProjectTicketParticipantsR shar proj ltkhid
|
|
||||||
, AP.ticketTeam =
|
|
||||||
Just $ encodeRouteLocal $ ProjectTicketTeamR shar proj ltkhid
|
|
||||||
, AP.ticketEvents =
|
|
||||||
encodeRouteLocal $ ProjectTicketEventsR shar proj ltkhid
|
|
||||||
, AP.ticketDeps =
|
|
||||||
encodeRouteLocal $ ProjectTicketDepsR shar proj ltkhid
|
|
||||||
, AP.ticketReverseDeps =
|
|
||||||
encodeRouteLocal $ ProjectTicketReverseDepsR shar proj ltkhid
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
, AP.ticketAttributedTo =
|
|
||||||
case author of
|
|
||||||
Left sharer ->
|
|
||||||
encodeRouteLocal $ SharerR $ sharerIdent sharer
|
|
||||||
Right (_inztance, object, _actor) ->
|
|
||||||
remoteObjectIdent object
|
|
||||||
, AP.ticketPublished = Just $ ticketCreated ticket
|
|
||||||
, AP.ticketUpdated = Nothing
|
|
||||||
, AP.ticketContext =
|
|
||||||
Just $ encodeRouteHome $ ProjectR shar proj
|
|
||||||
-- , AP.ticketName = Just $ "#" <> T.pack (show num)
|
|
||||||
, AP.ticketSummary = TextHtml $ ticketTitle ticket
|
|
||||||
, AP.ticketContent = TextHtml $ ticketDescription ticket
|
|
||||||
, AP.ticketSource = TextPandocMarkdown $ ticketSource ticket
|
|
||||||
, AP.ticketAssignedTo =
|
|
||||||
encodeRouteHome . SharerR . sharerIdent . fst <$> massignee
|
|
||||||
, AP.ticketResolved =
|
|
||||||
let u (Left (actor, obiid)) =
|
|
||||||
encodeRouteHome $
|
|
||||||
outboxItemRoute actor $ encodeKeyHashid obiid
|
|
||||||
u (Right (i, ro)) =
|
|
||||||
ObjURI (instanceHost i) (remoteObjectIdent ro)
|
|
||||||
in (,Nothing) . Just . u <$> mresolved
|
|
||||||
, AP.ticketAttachment = Nothing
|
|
||||||
}
|
|
||||||
provideHtmlAndAP' host ticketAP $
|
provideHtmlAndAP' host ticketAP $
|
||||||
let followButton =
|
let followButton =
|
||||||
followW
|
followW
|
||||||
|
@ -428,6 +274,174 @@ getProjectTicketR shar proj ltkhid = do
|
||||||
(ProjectTicketUnfollowR shar proj ltkhid)
|
(ProjectTicketUnfollowR shar proj ltkhid)
|
||||||
(return $ localTicketFollowers lticket)
|
(return $ localTicketFollowers lticket)
|
||||||
in $(widgetFile "ticket/one")
|
in $(widgetFile "ticket/one")
|
||||||
|
-}
|
||||||
|
|
||||||
|
getTicketDiscussionR
|
||||||
|
:: KeyHashid Deck -> KeyHashid TicketDeck -> Handler TypedContent
|
||||||
|
getTicketDiscussionR _ _ = do
|
||||||
|
error "Temporarily disabled"
|
||||||
|
{-
|
||||||
|
encodeHid <- getEncodeKeyHashid
|
||||||
|
getDiscussion
|
||||||
|
(ProjectTicketReplyR shar proj ltkhid . encodeHid)
|
||||||
|
(ProjectTicketTopReplyR shar proj ltkhid)
|
||||||
|
(selectDiscussionId shar proj ltkhid)
|
||||||
|
-}
|
||||||
|
|
||||||
|
getTicketEventsR
|
||||||
|
:: KeyHashid Deck -> KeyHashid TicketDeck -> Handler TypedContent
|
||||||
|
getTicketEventsR _ _ = do
|
||||||
|
error "Not implemented yet"
|
||||||
|
|
||||||
|
getTicketFollowersR
|
||||||
|
:: KeyHashid Deck -> KeyHashid TicketDeck -> Handler TypedContent
|
||||||
|
getTicketFollowersR deckHash ticketHash = getFollowersCollection here getFsid
|
||||||
|
where
|
||||||
|
here = TicketFollowersR deckHash ticketHash
|
||||||
|
getFsid = do
|
||||||
|
(_, _, Entity _ t, _, _) <- getTicket404 deckHash ticketHash
|
||||||
|
return $ ticketFollowers t
|
||||||
|
|
||||||
|
getTicketDepsR
|
||||||
|
:: KeyHashid Deck -> KeyHashid TicketDeck -> Handler TypedContent
|
||||||
|
getTicketDepsR deckHash ticketHash =
|
||||||
|
error "Temporarily disabled"
|
||||||
|
{-
|
||||||
|
getDependencyCollection here dep getLocalTicketId404
|
||||||
|
where
|
||||||
|
here = TicketDepsR deckHash ticketHash
|
||||||
|
dep = TicketDepR deckHash ticketHash
|
||||||
|
getLocalTicketId404 = do
|
||||||
|
(_, _, Entity ltid _, _, _, _, _) <- getTicket404 dkhid ltkhid
|
||||||
|
return ltid
|
||||||
|
-}
|
||||||
|
|
||||||
|
getTicketReverseDepsR
|
||||||
|
:: KeyHashid Deck -> KeyHashid TicketDeck -> Handler TypedContent
|
||||||
|
getTicketReverseDepsR deckHash ticketHash =
|
||||||
|
error "Temporarily disabled"
|
||||||
|
{-
|
||||||
|
getReverseDependencyCollection here getLocalTicketId404
|
||||||
|
where
|
||||||
|
here = TicketReverseDepsR deckhash ticketHash
|
||||||
|
getLocalTicketId404 = do
|
||||||
|
(_, _, _, Entity ltid _, _, _, _, _) <- getTicket404 deckHash ticketHash
|
||||||
|
return ltid
|
||||||
|
-}
|
||||||
|
|
||||||
|
getTicketDepR
|
||||||
|
:: KeyHashid Deck
|
||||||
|
-> KeyHashid TicketDeck
|
||||||
|
-> KeyHashid LocalTicketDependency
|
||||||
|
-> Handler TypedContent
|
||||||
|
getTicketDepR _ _ _ = do
|
||||||
|
error "Temporarily disabled"
|
||||||
|
{-
|
||||||
|
encodeRouteLocal <- getEncodeRouteLocal
|
||||||
|
encodeRouteHome <- getEncodeRouteHome
|
||||||
|
wiRoute <- askWorkItemRoute
|
||||||
|
hLocal <- asksSite siteInstanceHost
|
||||||
|
|
||||||
|
tdid <- decodeKeyHashid404 tdkhid
|
||||||
|
(td, author, parent, child) <- runDB $ do
|
||||||
|
td <- get404 tdid
|
||||||
|
(td,,,)
|
||||||
|
<$> getAuthor tdid
|
||||||
|
<*> getWorkItem ( localTicketDependencyParent td)
|
||||||
|
<*> getChild tdid
|
||||||
|
let host =
|
||||||
|
case author of
|
||||||
|
Left _ -> hLocal
|
||||||
|
Right (h, _) -> h
|
||||||
|
tdepAP = AP.TicketDependency
|
||||||
|
{ ticketDepId = Just $ encodeRouteHome here
|
||||||
|
, ticketDepParent = encodeRouteHome $ wiRoute parent
|
||||||
|
, ticketDepChild =
|
||||||
|
case child of
|
||||||
|
Left wi -> encodeRouteHome $ wiRoute wi
|
||||||
|
Right (h, lu) -> ObjURI h lu
|
||||||
|
, ticketDepAttributedTo =
|
||||||
|
case author of
|
||||||
|
Left shr -> encodeRouteLocal $ SharerR shr
|
||||||
|
Right (_h, lu) -> lu
|
||||||
|
, ticketDepPublished = Just $ localTicketDependencyCreated td
|
||||||
|
, ticketDepUpdated = Nothing
|
||||||
|
}
|
||||||
|
provideHtmlAndAP' host tdepAP $ redirectToPrettyJSON here
|
||||||
|
where
|
||||||
|
here = TicketDepR tdkhid
|
||||||
|
getAuthor tdid = do
|
||||||
|
tda <- requireEitherAlt
|
||||||
|
(getValBy $ UniqueTicketDependencyAuthorLocal tdid)
|
||||||
|
(getValBy $ UniqueTicketDependencyAuthorRemote tdid)
|
||||||
|
"No TDA"
|
||||||
|
"Both TDAL and TDAR"
|
||||||
|
bitraverse
|
||||||
|
(\ tdal -> do
|
||||||
|
p <- getJust $ ticketDependencyAuthorLocalAuthor tdal
|
||||||
|
s <- getJust $ personIdent p
|
||||||
|
return $ sharerIdent s
|
||||||
|
)
|
||||||
|
(\ tdar -> do
|
||||||
|
ra <- getJust $ ticketDependencyAuthorRemoteAuthor tdar
|
||||||
|
ro <- getJust $ remoteActorIdent ra
|
||||||
|
i <- getJust $ remoteObjectInstance ro
|
||||||
|
return (instanceHost i, remoteObjectIdent ro)
|
||||||
|
)
|
||||||
|
tda
|
||||||
|
getChild tdid = do
|
||||||
|
tdc <- requireEitherAlt
|
||||||
|
(getValBy $ UniqueTicketDependencyChildLocal tdid)
|
||||||
|
(getValBy $ UniqueTicketDependencyChildRemote tdid)
|
||||||
|
"No TDC"
|
||||||
|
"Both TDCL and TDCR"
|
||||||
|
bitraverse
|
||||||
|
(getWorkItem . ticketDependencyChildLocalChild)
|
||||||
|
(\ tdcr -> do
|
||||||
|
ro <- getJust $ ticketDependencyChildRemoteChild tdcr
|
||||||
|
i <- getJust $ remoteObjectInstance ro
|
||||||
|
return (instanceHost i, remoteObjectIdent ro)
|
||||||
|
)
|
||||||
|
tdc
|
||||||
|
-}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{-
|
||||||
|
getProjectTicketNewR :: ShrIdent -> PrjIdent -> Handler Html
|
||||||
|
getProjectTicketNewR shr prj = do
|
||||||
|
wid <- runDB $ do
|
||||||
|
Entity sid _ <- getBy404 $ UniqueSharer shr
|
||||||
|
Entity _ j <- getBy404 $ UniqueProject prj sid
|
||||||
|
return $ projectWorkflow j
|
||||||
|
((_result, widget), enctype) <- runFormPost $ newTicketForm wid
|
||||||
|
defaultLayout $(widgetFile "ticket/new")
|
||||||
|
|
||||||
putProjectTicketR :: ShrIdent -> PrjIdent -> KeyHashid LocalTicket -> Handler Html
|
putProjectTicketR :: ShrIdent -> PrjIdent -> KeyHashid LocalTicket -> Handler Html
|
||||||
putProjectTicketR shr prj ltkhid = do
|
putProjectTicketR shr prj ltkhid = do
|
||||||
|
@ -757,15 +771,6 @@ selectDiscussionId shr prj ltkhid = do
|
||||||
(_es, _ej, _et, Entity _ lticket, _etcl, _etpl, _author, _) <- getProjectTicket404 shr prj ltkhid
|
(_es, _ej, _et, Entity _ lticket, _etcl, _etpl, _author, _) <- getProjectTicket404 shr prj ltkhid
|
||||||
return $ localTicketDiscuss lticket
|
return $ localTicketDiscuss lticket
|
||||||
|
|
||||||
getProjectTicketDiscussionR
|
|
||||||
:: ShrIdent -> PrjIdent -> KeyHashid LocalTicket -> Handler Html
|
|
||||||
getProjectTicketDiscussionR shar proj ltkhid = do
|
|
||||||
encodeHid <- getEncodeKeyHashid
|
|
||||||
getDiscussion
|
|
||||||
(ProjectTicketReplyR shar proj ltkhid . encodeHid)
|
|
||||||
(ProjectTicketTopReplyR shar proj ltkhid)
|
|
||||||
(selectDiscussionId shar proj ltkhid)
|
|
||||||
|
|
||||||
postProjectTicketDiscussionR
|
postProjectTicketDiscussionR
|
||||||
:: ShrIdent -> PrjIdent -> KeyHashid LocalTicket -> Handler Html
|
:: ShrIdent -> PrjIdent -> KeyHashid LocalTicket -> Handler Html
|
||||||
postProjectTicketDiscussionR shr prj ltkhid = do
|
postProjectTicketDiscussionR shr prj ltkhid = do
|
||||||
|
@ -828,16 +833,6 @@ getProjectTicketReplyR shr prj ltkhid mkhid = do
|
||||||
(selectDiscussionId shr prj ltkhid)
|
(selectDiscussionId shr prj ltkhid)
|
||||||
mid
|
mid
|
||||||
|
|
||||||
getProjectTicketDepsR
|
|
||||||
:: ShrIdent -> PrjIdent -> KeyHashid LocalTicket -> Handler TypedContent
|
|
||||||
getProjectTicketDepsR shr prj ltkhid =
|
|
||||||
getDependencyCollection here getLocalTicketId404
|
|
||||||
where
|
|
||||||
here = ProjectTicketDepsR shr prj ltkhid
|
|
||||||
getLocalTicketId404 = do
|
|
||||||
(_, _, _, Entity ltid _, _, _, _, _) <- getProjectTicket404 shr prj ltkhid
|
|
||||||
return ltid
|
|
||||||
|
|
||||||
postProjectTicketDepsR
|
postProjectTicketDepsR
|
||||||
:: ShrIdent -> PrjIdent -> KeyHashid LocalTicket -> Handler Html
|
:: ShrIdent -> PrjIdent -> KeyHashid LocalTicket -> Handler Html
|
||||||
postProjectTicketDepsR _shr _prj _ltkhid = error "Temporarily disabled"
|
postProjectTicketDepsR _shr _prj _ltkhid = error "Temporarily disabled"
|
||||||
|
@ -908,85 +903,6 @@ deleteTicketDepOldR _shr _prj _pnum _cnum = error "Dep deletion disabled for now
|
||||||
redirect $ ProjectTicketDepsR shr prj pnum
|
redirect $ ProjectTicketDepsR shr prj pnum
|
||||||
-}
|
-}
|
||||||
|
|
||||||
getProjectTicketReverseDepsR
|
|
||||||
:: ShrIdent -> PrjIdent -> KeyHashid LocalTicket -> Handler TypedContent
|
|
||||||
getProjectTicketReverseDepsR shr prj ltkhid =
|
|
||||||
getReverseDependencyCollection here getLocalTicketId404
|
|
||||||
where
|
|
||||||
here = ProjectTicketReverseDepsR shr prj ltkhid
|
|
||||||
getLocalTicketId404 = do
|
|
||||||
(_, _, _, Entity ltid _, _, _, _, _) <- getProjectTicket404 shr prj ltkhid
|
|
||||||
return ltid
|
|
||||||
|
|
||||||
getTicketDepR :: KeyHashid LocalTicketDependency -> Handler TypedContent
|
|
||||||
getTicketDepR tdkhid = do
|
|
||||||
encodeRouteLocal <- getEncodeRouteLocal
|
|
||||||
encodeRouteHome <- getEncodeRouteHome
|
|
||||||
wiRoute <- askWorkItemRoute
|
|
||||||
hLocal <- asksSite siteInstanceHost
|
|
||||||
|
|
||||||
tdid <- decodeKeyHashid404 tdkhid
|
|
||||||
(td, author, parent, child) <- runDB $ do
|
|
||||||
td <- get404 tdid
|
|
||||||
(td,,,)
|
|
||||||
<$> getAuthor tdid
|
|
||||||
<*> getWorkItem ( localTicketDependencyParent td)
|
|
||||||
<*> getChild tdid
|
|
||||||
let host =
|
|
||||||
case author of
|
|
||||||
Left _ -> hLocal
|
|
||||||
Right (h, _) -> h
|
|
||||||
tdepAP = AP.TicketDependency
|
|
||||||
{ ticketDepId = Just $ encodeRouteHome here
|
|
||||||
, ticketDepParent = encodeRouteHome $ wiRoute parent
|
|
||||||
, ticketDepChild =
|
|
||||||
case child of
|
|
||||||
Left wi -> encodeRouteHome $ wiRoute wi
|
|
||||||
Right (h, lu) -> ObjURI h lu
|
|
||||||
, ticketDepAttributedTo =
|
|
||||||
case author of
|
|
||||||
Left shr -> encodeRouteLocal $ SharerR shr
|
|
||||||
Right (_h, lu) -> lu
|
|
||||||
, ticketDepPublished = Just $ localTicketDependencyCreated td
|
|
||||||
, ticketDepUpdated = Nothing
|
|
||||||
}
|
|
||||||
provideHtmlAndAP' host tdepAP $ redirectToPrettyJSON here
|
|
||||||
where
|
|
||||||
here = TicketDepR tdkhid
|
|
||||||
getAuthor tdid = do
|
|
||||||
tda <- requireEitherAlt
|
|
||||||
(getValBy $ UniqueTicketDependencyAuthorLocal tdid)
|
|
||||||
(getValBy $ UniqueTicketDependencyAuthorRemote tdid)
|
|
||||||
"No TDA"
|
|
||||||
"Both TDAL and TDAR"
|
|
||||||
bitraverse
|
|
||||||
(\ tdal -> do
|
|
||||||
p <- getJust $ ticketDependencyAuthorLocalAuthor tdal
|
|
||||||
s <- getJust $ personIdent p
|
|
||||||
return $ sharerIdent s
|
|
||||||
)
|
|
||||||
(\ tdar -> do
|
|
||||||
ra <- getJust $ ticketDependencyAuthorRemoteAuthor tdar
|
|
||||||
ro <- getJust $ remoteActorIdent ra
|
|
||||||
i <- getJust $ remoteObjectInstance ro
|
|
||||||
return (instanceHost i, remoteObjectIdent ro)
|
|
||||||
)
|
|
||||||
tda
|
|
||||||
getChild tdid = do
|
|
||||||
tdc <- requireEitherAlt
|
|
||||||
(getValBy $ UniqueTicketDependencyChildLocal tdid)
|
|
||||||
(getValBy $ UniqueTicketDependencyChildRemote tdid)
|
|
||||||
"No TDC"
|
|
||||||
"Both TDCL and TDCR"
|
|
||||||
bitraverse
|
|
||||||
(getWorkItem . ticketDependencyChildLocalChild)
|
|
||||||
(\ tdcr -> do
|
|
||||||
ro <- getJust $ ticketDependencyChildRemoteChild tdcr
|
|
||||||
i <- getJust $ remoteObjectInstance ro
|
|
||||||
return (instanceHost i, remoteObjectIdent ro)
|
|
||||||
)
|
|
||||||
tdc
|
|
||||||
|
|
||||||
getProjectTicketParticipantsR
|
getProjectTicketParticipantsR
|
||||||
:: ShrIdent -> PrjIdent -> KeyHashid LocalTicket -> Handler TypedContent
|
:: ShrIdent -> PrjIdent -> KeyHashid LocalTicket -> Handler TypedContent
|
||||||
getProjectTicketParticipantsR shr prj ltkhid = getFollowersCollection here getFsid
|
getProjectTicketParticipantsR shr prj ltkhid = getFollowersCollection here getFsid
|
||||||
|
@ -1034,10 +950,6 @@ getProjectTicketTeamR shr prj ltkhid = do
|
||||||
}
|
}
|
||||||
provideHtmlAndAP team $ redirectToPrettyJSON here
|
provideHtmlAndAP team $ redirectToPrettyJSON here
|
||||||
|
|
||||||
getProjectTicketEventsR
|
|
||||||
:: ShrIdent -> PrjIdent -> KeyHashid LocalTicket -> Handler TypedContent
|
|
||||||
getProjectTicketEventsR _shr _prj _ltkhid = error "TODO not implemented"
|
|
||||||
|
|
||||||
getSharerTicketsR :: ShrIdent -> Handler TypedContent
|
getSharerTicketsR :: ShrIdent -> Handler TypedContent
|
||||||
getSharerTicketsR =
|
getSharerTicketsR =
|
||||||
getSharerWorkItems SharerTicketsR SharerTicketR countTickets selectTickets
|
getSharerWorkItems SharerTicketsR SharerTicketR countTickets selectTickets
|
||||||
|
@ -1197,15 +1109,6 @@ getSharerTicketReverseDepsR shr talkhid =
|
||||||
(_, Entity ltid _, _, _, _) <- getSharerTicket404 shr talkhid
|
(_, Entity ltid _, _, _, _) <- getSharerTicket404 shr talkhid
|
||||||
return ltid
|
return ltid
|
||||||
|
|
||||||
getSharerTicketFollowersR
|
|
||||||
:: ShrIdent -> KeyHashid TicketAuthorLocal -> Handler TypedContent
|
|
||||||
getSharerTicketFollowersR shr talkhid = getFollowersCollection here getFsid
|
|
||||||
where
|
|
||||||
here = SharerTicketFollowersR shr talkhid
|
|
||||||
getFsid = do
|
|
||||||
(_, Entity _ lt, _, _, _) <- getSharerTicket404 shr talkhid
|
|
||||||
return $ localTicketFollowers lt
|
|
||||||
|
|
||||||
getSharerTicketTeamR
|
getSharerTicketTeamR
|
||||||
:: ShrIdent -> KeyHashid TicketAuthorLocal -> Handler TypedContent
|
:: ShrIdent -> KeyHashid TicketAuthorLocal -> Handler TypedContent
|
||||||
getSharerTicketTeamR shr talkhid = do
|
getSharerTicketTeamR shr talkhid = do
|
||||||
|
@ -1221,3 +1124,4 @@ getSharerTicketEventsR shr talkhid = do
|
||||||
provideEmptyCollection
|
provideEmptyCollection
|
||||||
CollectionTypeOrdered
|
CollectionTypeOrdered
|
||||||
(SharerTicketEventsR shr talkhid)
|
(SharerTicketEventsR shr talkhid)
|
||||||
|
-}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{- This file is part of Vervis.
|
{- This file is part of Vervis.
|
||||||
-
|
-
|
||||||
- Written in 2019 by fr33domlover <fr33domlover@riseup.net>.
|
- Written in 2019, 2022 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.
|
||||||
-
|
-
|
||||||
|
@ -135,7 +135,6 @@ instance ToJSON Commit
|
||||||
data Push = Push
|
data Push = Push
|
||||||
{ pushSecret :: Text
|
{ pushSecret :: Text
|
||||||
, pushUser :: Int64
|
, pushUser :: Int64
|
||||||
, pushSharer :: Text
|
|
||||||
, pushRepo :: Text
|
, pushRepo :: Text
|
||||||
, pushBranch :: Maybe Text
|
, pushBranch :: Maybe Text
|
||||||
, pushBefore :: Maybe Text
|
, pushBefore :: Maybe Text
|
||||||
|
@ -200,8 +199,8 @@ sendPush config manager push = do
|
||||||
adaptErr = T.pack . displayException
|
adaptErr = T.pack . displayException
|
||||||
consHeader n b r = r { requestHeaders = (n, b) : requestHeaders r }
|
consHeader n b r = r { requestHeaders = (n, b) : requestHeaders r }
|
||||||
|
|
||||||
reportNewCommits :: Config -> Text -> Text -> IO ()
|
reportNewCommits :: Config -> Text -> IO ()
|
||||||
reportNewCommits config sharer repo = do
|
reportNewCommits config repo = do
|
||||||
user <- read <$> getEnv "VERVIS_SSH_USER"
|
user <- read <$> getEnv "VERVIS_SSH_USER"
|
||||||
manager <- newManager defaultManagerSettings
|
manager <- newManager defaultManagerSettings
|
||||||
withRepo "." $ loop user manager
|
withRepo "." $ loop user manager
|
||||||
|
@ -251,7 +250,6 @@ reportNewCommits config sharer repo = do
|
||||||
let push = Push
|
let push = Push
|
||||||
{ pushSecret = configSecret config
|
{ pushSecret = configSecret config
|
||||||
, pushUser = user
|
, pushUser = user
|
||||||
, pushSharer = sharer
|
|
||||||
, pushRepo = repo
|
, pushRepo = repo
|
||||||
, pushBranch = Just branch
|
, pushBranch = Just branch
|
||||||
, pushBefore = old <$ moldRef
|
, pushBefore = old <$ moldRef
|
||||||
|
@ -306,10 +304,10 @@ reportNewCommits config sharer repo = do
|
||||||
|
|
||||||
postReceive :: IO ()
|
postReceive :: IO ()
|
||||||
postReceive = do
|
postReceive = do
|
||||||
(host, sharer, repo) <- do
|
(host, repo) <- do
|
||||||
args <- getArgs
|
args <- getArgs
|
||||||
case args of
|
case args of
|
||||||
[h, s, r] -> return (h, T.pack s, T.pack r)
|
[h, r] -> return (h, T.pack r)
|
||||||
_ -> die "Unexpected number of arguments"
|
_ -> die "Unexpected number of arguments"
|
||||||
cachePath <- getVervisCachePath host
|
cachePath <- getVervisCachePath host
|
||||||
config <- do
|
config <- do
|
||||||
|
@ -317,10 +315,10 @@ postReceive = do
|
||||||
case mc of
|
case mc of
|
||||||
Nothing -> die "Parsing hook config failed"
|
Nothing -> die "Parsing hook config failed"
|
||||||
Just c -> return c
|
Just c -> return c
|
||||||
reportNewCommits config sharer repo
|
reportNewCommits config repo
|
||||||
|
|
||||||
reportNewPatches :: Config -> Text -> Text -> IO ()
|
reportNewPatches :: Config -> Text -> IO ()
|
||||||
reportNewPatches config sharer repo = do
|
reportNewPatches config repo = do
|
||||||
user <- read <$> getEnv "VERVIS_SSH_USER"
|
user <- read <$> getEnv "VERVIS_SSH_USER"
|
||||||
manager <- newManager defaultManagerSettings
|
manager <- newManager defaultManagerSettings
|
||||||
melem <- parseXMLDoc <$> getEnv "DARCS_PATCHES_XML"
|
melem <- parseXMLDoc <$> getEnv "DARCS_PATCHES_XML"
|
||||||
|
@ -333,7 +331,6 @@ reportNewPatches config sharer repo = do
|
||||||
return Push
|
return Push
|
||||||
{ pushSecret = configSecret config
|
{ pushSecret = configSecret config
|
||||||
, pushUser = user
|
, pushUser = user
|
||||||
, pushSharer = sharer
|
|
||||||
, pushRepo = repo
|
, pushRepo = repo
|
||||||
, pushBranch = Nothing
|
, pushBranch = Nothing
|
||||||
, pushBefore = Nothing
|
, pushBefore = Nothing
|
||||||
|
@ -416,10 +413,10 @@ reportNewPatches config sharer repo = do
|
||||||
|
|
||||||
postApply :: IO ()
|
postApply :: IO ()
|
||||||
postApply = do
|
postApply = do
|
||||||
(host, sharer, repo) <- do
|
(host, repo) <- do
|
||||||
args <- getArgs
|
args <- getArgs
|
||||||
case args of
|
case args of
|
||||||
[h, s, r] -> return (h, T.pack s, T.pack r)
|
[h, r] -> return (h, T.pack r)
|
||||||
_ -> die "Unexpected number of arguments"
|
_ -> die "Unexpected number of arguments"
|
||||||
cachePath <- getVervisCachePath host
|
cachePath <- getVervisCachePath host
|
||||||
config <- do
|
config <- do
|
||||||
|
@ -427,4 +424,4 @@ postApply = do
|
||||||
case mc of
|
case mc of
|
||||||
Nothing -> die "Parsing hook config failed"
|
Nothing -> die "Parsing hook config failed"
|
||||||
Just c -> return c
|
Just c -> return c
|
||||||
reportNewPatches config sharer repo
|
reportNewPatches config repo
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -14,6 +14,7 @@
|
||||||
-}
|
-}
|
||||||
|
|
||||||
module Vervis.Migration.Model
|
module Vervis.Migration.Model
|
||||||
|
{-
|
||||||
( EntityField (..)
|
( EntityField (..)
|
||||||
, Unique (..)
|
, Unique (..)
|
||||||
, model_2016_08_04
|
, model_2016_08_04
|
||||||
|
@ -282,25 +283,30 @@ module Vervis.Migration.Model
|
||||||
, Repo300Generic (..)
|
, Repo300Generic (..)
|
||||||
, CollabFulfillsLocalTopicCreation300Generic (..)
|
, CollabFulfillsLocalTopicCreation300Generic (..)
|
||||||
)
|
)
|
||||||
|
-}
|
||||||
where
|
where
|
||||||
|
|
||||||
import Data.ByteString (ByteString)
|
import Data.ByteString (ByteString)
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
import Data.Time (UTCTime)
|
import Data.Time (UTCTime)
|
||||||
import Database.Persist.Class (EntityField, Unique)
|
import Database.Persist.Class (EntityField, Unique)
|
||||||
|
import Database.Persist.EmailAddress ()
|
||||||
import Database.Persist.Schema.Types (Entity)
|
import Database.Persist.Schema.Types (Entity)
|
||||||
import Database.Persist.Schema.SQL ()
|
import Database.Persist.Schema.SQL ()
|
||||||
import Database.Persist.Schema.TH (makeEntitiesMigration)
|
import Database.Persist.Schema.TH (makeEntitiesMigration)
|
||||||
import Database.Persist.Sql (SqlBackend)
|
import Database.Persist.Sql (SqlBackend)
|
||||||
|
import Text.Email.Validate (EmailAddress)
|
||||||
|
|
||||||
|
import Development.PatchMediaType
|
||||||
|
import Development.PatchMediaType.Persist
|
||||||
|
|
||||||
import Vervis.FedURI
|
import Vervis.FedURI
|
||||||
import Vervis.Migration.TH (schema)
|
import Vervis.Migration.TH (schema)
|
||||||
import Vervis.Model (SharerId)
|
|
||||||
import Vervis.Model.Group
|
import Vervis.Model.Group
|
||||||
import Vervis.Model.Ident
|
import Vervis.Model.Ident
|
||||||
import Development.PatchMediaType
|
|
||||||
import Vervis.Model.Role
|
import Vervis.Model.Role
|
||||||
import Vervis.Model.TH
|
import Vervis.Model.TH
|
||||||
|
import Vervis.Model.Ticket
|
||||||
import Vervis.Model.Workflow
|
import Vervis.Model.Workflow
|
||||||
|
|
||||||
-- For migrations 77, 114
|
-- For migrations 77, 114
|
||||||
|
@ -538,3 +544,82 @@ model_2022_07_24 = $(schema "2022_07_24_collab_fulfills")
|
||||||
|
|
||||||
makeEntitiesMigration "300"
|
makeEntitiesMigration "300"
|
||||||
$(modelFile "migrations/2022_07_25_collab_fulfills_mig.model")
|
$(modelFile "migrations/2022_07_25_collab_fulfills_mig.model")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
makeEntitiesMigration "303"
|
||||||
|
$(modelFile "migrations/303_2022-08-04_username.model")
|
||||||
|
|
||||||
|
makeEntitiesMigration "308"
|
||||||
|
$(modelFile "migrations/308_2022-08-04_remove_tcr.model")
|
||||||
|
|
||||||
|
makeEntitiesMigration "310"
|
||||||
|
$(modelFile "migrations/310_2022-08-04_move_ticket_discuss.model")
|
||||||
|
|
||||||
|
makeEntitiesMigration "312"
|
||||||
|
$(modelFile "migrations/312_2022-08-04_move_ticket_followers.model")
|
||||||
|
|
||||||
|
makeEntitiesMigration "316"
|
||||||
|
$(modelFile "migrations/316_2022-08-04_move_ticket_accept.model")
|
||||||
|
|
||||||
|
makeEntitiesMigration "318"
|
||||||
|
$(modelFile "migrations/318_2022-08-04_tal_ticket.model")
|
||||||
|
|
||||||
|
makeEntitiesMigration "323"
|
||||||
|
$(modelFile "migrations/323_2022-08-04_tar_ticket.model")
|
||||||
|
|
||||||
|
makeEntitiesMigration "328"
|
||||||
|
$(modelFile "migrations/328_2022-08-04_tjl_ticket.model")
|
||||||
|
|
||||||
|
makeEntitiesMigration "332"
|
||||||
|
$(modelFile "migrations/332_2022-08-04_trl_ticket.model")
|
||||||
|
|
||||||
|
makeEntitiesMigration "338"
|
||||||
|
$(modelFile "migrations/338_2022-08-04_rtd_child.model")
|
||||||
|
|
||||||
|
makeEntitiesMigration "342"
|
||||||
|
$(modelFile "migrations/342_2022-08-04_ltd_parent.model")
|
||||||
|
|
||||||
|
makeEntitiesMigration "345"
|
||||||
|
$(modelFile "migrations/345_2022-08-04_tdcl_child.model")
|
||||||
|
|
||||||
|
makeEntitiesMigration "348"
|
||||||
|
$(modelFile "migrations/348_2022-08-04_tr_ticket.model")
|
||||||
|
|
||||||
|
makeEntitiesMigration "356"
|
||||||
|
$(modelFile "migrations/356_2022-08-04_person_actor.model")
|
||||||
|
|
||||||
|
makeEntitiesMigration "365"
|
||||||
|
$(modelFile "migrations/365_2022-08-04_group_actor.model")
|
||||||
|
|
||||||
|
makeEntitiesMigration "367"
|
||||||
|
$(modelFile "migrations/367_2022-08-04_repo_actor.model")
|
||||||
|
|
||||||
|
model_384_loom :: [Entity SqlBackend]
|
||||||
|
model_384_loom = $(schema "384_2022-08-04_loom")
|
||||||
|
|
||||||
|
model_386_assignee :: [Entity SqlBackend]
|
||||||
|
model_386_assignee = $(schema "386_2022-08-04_assignee")
|
||||||
|
|
||||||
|
makeEntitiesMigration "388"
|
||||||
|
$(modelFile "migrations/388_2022-08-04_ticket_loom.model")
|
||||||
|
|
||||||
|
makeEntitiesMigration "396"
|
||||||
|
$(modelFile "migrations/396_2022-08-04_repo_dir.model")
|
||||||
|
|
||||||
|
model_399_fwder :: [Entity SqlBackend]
|
||||||
|
model_399_fwder = $(schema "399_2022-08-04_fwder")
|
||||||
|
|
||||||
|
model_408_collab_loom :: [Entity SqlBackend]
|
||||||
|
model_408_collab_loom = $(schema "408_2022-08-04_collab_loom")
|
||||||
|
|
||||||
|
makeEntitiesMigration "409"
|
||||||
|
$(modelFile "migrations/409_2022-08-05_repo_create.model")
|
||||||
|
|
||||||
|
makeEntitiesMigration "414"
|
||||||
|
$(modelFile "migrations/414_2022-08-05_followremote_actor.model")
|
||||||
|
|
||||||
|
makeEntitiesMigration "418"
|
||||||
|
$(modelFile "migrations/418_2022-08-06_follow_actor.model")
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{- This file is part of Vervis.
|
{- This file is part of Vervis.
|
||||||
-
|
-
|
||||||
- Written in 2016 by fr33domlover <fr33domlover@riseup.net>.
|
- Written in 2016, 2022 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.
|
||||||
-
|
-
|
||||||
|
@ -16,7 +16,11 @@
|
||||||
-- | Dedicated identifier name types for type safety. For use in routes, models
|
-- | Dedicated identifier name types for type safety. For use in routes, models
|
||||||
-- and handlers.
|
-- and handlers.
|
||||||
module Vervis.Model.Ident
|
module Vervis.Model.Ident
|
||||||
( ShrIdent (..)
|
( Username (..)
|
||||||
|
, username2text
|
||||||
|
, text2username
|
||||||
|
|
||||||
|
, ShrIdent (..)
|
||||||
, shr2text
|
, shr2text
|
||||||
, text2shr
|
, text2shr
|
||||||
, KyIdent (..)
|
, KyIdent (..)
|
||||||
|
@ -57,6 +61,16 @@ import Database.Persist.Class.Local ()
|
||||||
import Database.Persist.Sql.Local ()
|
import Database.Persist.Sql.Local ()
|
||||||
import Web.PathPieces.Local ()
|
import Web.PathPieces.Local ()
|
||||||
|
|
||||||
|
newtype Username = Username { unUsername :: CI Text }
|
||||||
|
deriving
|
||||||
|
(Eq, Ord, Show, Read, PersistField, PersistFieldSql, SqlString, PathPiece)
|
||||||
|
|
||||||
|
username2text :: Username -> Text
|
||||||
|
username2text = CI.original . unUsername
|
||||||
|
|
||||||
|
text2username :: Text -> Username
|
||||||
|
text2username = Username . CI.mk
|
||||||
|
|
||||||
newtype ShrIdent = ShrIdent { unShrIdent :: CI Text }
|
newtype ShrIdent = ShrIdent { unShrIdent :: CI Text }
|
||||||
deriving
|
deriving
|
||||||
(Eq, Ord, Show, Read, PersistField, PersistFieldSql, SqlString, PathPiece)
|
(Eq, Ord, Show, Read, PersistField, PersistFieldSql, SqlString, PathPiece)
|
||||||
|
|
|
@ -1,232 +0,0 @@
|
||||||
{- 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.Patch
|
|
||||||
( getSharerProposal
|
|
||||||
, getSharerProposal404
|
|
||||||
, getRepoProposal
|
|
||||||
, getRepoProposal404
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Control.Monad
|
|
||||||
import Control.Monad.IO.Class
|
|
||||||
import Control.Monad.Trans.Class
|
|
||||||
import Control.Monad.Trans.Maybe
|
|
||||||
import Control.Monad.Trans.Reader
|
|
||||||
import Data.List.NonEmpty (NonEmpty, nonEmpty)
|
|
||||||
import Data.Maybe
|
|
||||||
import Data.Traversable
|
|
||||||
import Database.Persist
|
|
||||||
import Database.Persist.Sql
|
|
||||||
import Yesod.Core
|
|
||||||
|
|
||||||
import Yesod.Hashids
|
|
||||||
|
|
||||||
import Data.Either.Local
|
|
||||||
import Database.Persist.Local
|
|
||||||
|
|
||||||
import Vervis.Foundation
|
|
||||||
import Vervis.Model
|
|
||||||
import Vervis.Model.Ident
|
|
||||||
|
|
||||||
getResolved
|
|
||||||
:: MonadIO m
|
|
||||||
=> LocalTicketId
|
|
||||||
-> ReaderT SqlBackend m
|
|
||||||
(Maybe
|
|
||||||
( Entity TicketResolve
|
|
||||||
, Either (Entity TicketResolveLocal) (Entity TicketResolveRemote)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
getResolved ltid = do
|
|
||||||
metr <- getBy $ UniqueTicketResolve ltid
|
|
||||||
for metr $ \ etr@(Entity trid _) ->
|
|
||||||
(etr,) <$>
|
|
||||||
requireEitherAlt
|
|
||||||
(getBy $ UniqueTicketResolveLocal trid)
|
|
||||||
(getBy $ UniqueTicketResolveRemote trid)
|
|
||||||
"No TRX"
|
|
||||||
"Both TRL and TRR"
|
|
||||||
|
|
||||||
getSharerProposal
|
|
||||||
:: MonadIO m
|
|
||||||
=> ShrIdent
|
|
||||||
-> TicketAuthorLocalId
|
|
||||||
-> ReaderT SqlBackend m
|
|
||||||
( Maybe
|
|
||||||
( Entity TicketAuthorLocal
|
|
||||||
, Entity LocalTicket
|
|
||||||
, Entity Ticket
|
|
||||||
, Either
|
|
||||||
( Entity TicketContextLocal
|
|
||||||
, Entity TicketRepoLocal
|
|
||||||
)
|
|
||||||
( Entity TicketProjectRemote
|
|
||||||
, Maybe (Entity TicketProjectRemoteAccept)
|
|
||||||
)
|
|
||||||
, Maybe
|
|
||||||
( Entity TicketResolve
|
|
||||||
, Either
|
|
||||||
(Entity TicketResolveLocal)
|
|
||||||
(Entity TicketResolveRemote)
|
|
||||||
)
|
|
||||||
, NonEmpty BundleId
|
|
||||||
)
|
|
||||||
)
|
|
||||||
getSharerProposal 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
|
|
||||||
bnids <-
|
|
||||||
MaybeT $
|
|
||||||
nonEmpty <$> selectKeysList [BundleTicket ==. tid] [Desc BundleId]
|
|
||||||
repo <-
|
|
||||||
requireEitherAlt
|
|
||||||
(do mtcl <- lift $ getBy $ UniqueTicketContextLocal tid
|
|
||||||
for mtcl $ \ etcl@(Entity tclid _) -> 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"
|
|
||||||
mresolved <- lift $ getResolved ltid
|
|
||||||
return (Entity talid tal, Entity ltid lt, Entity tid t, repo, mresolved, bnids)
|
|
||||||
|
|
||||||
getSharerProposal404
|
|
||||||
:: ShrIdent
|
|
||||||
-> KeyHashid TicketAuthorLocal
|
|
||||||
-> AppDB
|
|
||||||
( Entity TicketAuthorLocal
|
|
||||||
, Entity LocalTicket
|
|
||||||
, Entity Ticket
|
|
||||||
, Either
|
|
||||||
( Entity TicketContextLocal
|
|
||||||
, Entity TicketRepoLocal
|
|
||||||
)
|
|
||||||
( Entity TicketProjectRemote
|
|
||||||
, Maybe (Entity TicketProjectRemoteAccept)
|
|
||||||
)
|
|
||||||
, Maybe
|
|
||||||
( Entity TicketResolve
|
|
||||||
, Either
|
|
||||||
(Entity TicketResolveLocal)
|
|
||||||
(Entity TicketResolveRemote)
|
|
||||||
)
|
|
||||||
, NonEmpty BundleId
|
|
||||||
)
|
|
||||||
getSharerProposal404 shr talkhid = do
|
|
||||||
talid <- decodeKeyHashid404 talkhid
|
|
||||||
mpatch <- getSharerProposal shr talid
|
|
||||||
case mpatch of
|
|
||||||
Nothing -> notFound
|
|
||||||
Just patch -> return patch
|
|
||||||
|
|
||||||
getRepoProposal
|
|
||||||
:: MonadIO m
|
|
||||||
=> ShrIdent
|
|
||||||
-> RpIdent
|
|
||||||
-> LocalTicketId
|
|
||||||
-> ReaderT SqlBackend m
|
|
||||||
( Maybe
|
|
||||||
( Entity Sharer
|
|
||||||
, Entity Repo
|
|
||||||
, Entity Ticket
|
|
||||||
, Entity LocalTicket
|
|
||||||
, Entity TicketContextLocal
|
|
||||||
, Entity TicketRepoLocal
|
|
||||||
, Either
|
|
||||||
(Entity TicketAuthorLocal, Entity TicketUnderProject)
|
|
||||||
(Entity TicketAuthorRemote)
|
|
||||||
, Maybe
|
|
||||||
( Entity TicketResolve
|
|
||||||
, Either
|
|
||||||
(Entity TicketResolveLocal)
|
|
||||||
(Entity TicketResolveRemote)
|
|
||||||
)
|
|
||||||
, NonEmpty BundleId
|
|
||||||
)
|
|
||||||
)
|
|
||||||
getRepoProposal 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
|
|
||||||
bnids <-
|
|
||||||
MaybeT $
|
|
||||||
nonEmpty <$> selectKeysList [BundleTicket ==. tid] [Desc BundleId]
|
|
||||||
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"
|
|
||||||
mresolved <- lift $ getResolved ltid
|
|
||||||
return (es, er, Entity tid t, Entity ltid lt, etcl, etrl, author, mresolved, bnids)
|
|
||||||
|
|
||||||
getRepoProposal404
|
|
||||||
:: ShrIdent
|
|
||||||
-> RpIdent
|
|
||||||
-> KeyHashid LocalTicket
|
|
||||||
-> AppDB
|
|
||||||
( Entity Sharer
|
|
||||||
, Entity Repo
|
|
||||||
, Entity Ticket
|
|
||||||
, Entity LocalTicket
|
|
||||||
, Entity TicketContextLocal
|
|
||||||
, Entity TicketRepoLocal
|
|
||||||
, Either
|
|
||||||
(Entity TicketAuthorLocal, Entity TicketUnderProject)
|
|
||||||
(Entity TicketAuthorRemote)
|
|
||||||
, Maybe
|
|
||||||
( Entity TicketResolve
|
|
||||||
, Either
|
|
||||||
(Entity TicketResolveLocal)
|
|
||||||
(Entity TicketResolveRemote)
|
|
||||||
)
|
|
||||||
, NonEmpty BundleId
|
|
||||||
)
|
|
||||||
getRepoProposal404 shr rp ltkhid = do
|
|
||||||
ltid <- decodeKeyHashid404 ltkhid
|
|
||||||
mpatch <- getRepoProposal shr rp ltid
|
|
||||||
case mpatch of
|
|
||||||
Nothing -> notFound
|
|
||||||
Just patch -> return patch
|
|
|
@ -1,6 +1,6 @@
|
||||||
{- This file is part of Vervis.
|
{- This file is part of Vervis.
|
||||||
-
|
-
|
||||||
- Written in 2016, 2019 by fr33domlover <fr33domlover@riseup.net>.
|
- Written in 2016, 2019, 2022 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.
|
||||||
-
|
-
|
||||||
|
@ -15,8 +15,6 @@
|
||||||
|
|
||||||
module Vervis.Path
|
module Vervis.Path
|
||||||
( askRepoRootDir
|
( askRepoRootDir
|
||||||
, sharerDir
|
|
||||||
, askSharerDir
|
|
||||||
, repoDir
|
, repoDir
|
||||||
, askRepoDir
|
, askRepoDir
|
||||||
)
|
)
|
||||||
|
@ -28,30 +26,21 @@ import System.FilePath ((</>))
|
||||||
import qualified Data.CaseInsensitive as CI (foldedCase)
|
import qualified Data.CaseInsensitive as CI (foldedCase)
|
||||||
import qualified Data.Text as T (unpack)
|
import qualified Data.Text as T (unpack)
|
||||||
|
|
||||||
|
import Yesod.Hashids
|
||||||
import Yesod.MonadSite
|
import Yesod.MonadSite
|
||||||
|
|
||||||
import Vervis.Foundation
|
import Vervis.Foundation
|
||||||
import Vervis.Model.Ident
|
import Vervis.Model
|
||||||
import Vervis.Settings
|
import Vervis.Settings
|
||||||
|
|
||||||
askRepoRootDir :: (MonadSite m, SiteEnv m ~ App) => m FilePath
|
askRepoRootDir :: (MonadSite m, SiteEnv m ~ App) => m FilePath
|
||||||
askRepoRootDir = asksSite $ appRepoDir . appSettings
|
askRepoRootDir = asksSite $ appRepoDir . appSettings
|
||||||
|
|
||||||
sharerDir :: FilePath -> ShrIdent -> FilePath
|
repoDir :: FilePath -> KeyHashid Repo -> FilePath
|
||||||
sharerDir root sharer =
|
repoDir root repo = root </> (T.unpack $ keyHashidText repo)
|
||||||
root </> (T.unpack $ CI.foldedCase $ unShrIdent sharer)
|
|
||||||
|
|
||||||
askSharerDir :: (MonadSite m, SiteEnv m ~ App) => ShrIdent -> m FilePath
|
|
||||||
askSharerDir sharer = do
|
|
||||||
root <- askRepoRootDir
|
|
||||||
return $ sharerDir root sharer
|
|
||||||
|
|
||||||
repoDir :: FilePath -> ShrIdent -> RpIdent -> FilePath
|
|
||||||
repoDir root sharer repo =
|
|
||||||
sharerDir root sharer </> (T.unpack $ CI.foldedCase $ unRpIdent repo)
|
|
||||||
|
|
||||||
askRepoDir
|
askRepoDir
|
||||||
:: (MonadSite m, SiteEnv m ~ App) => ShrIdent -> RpIdent -> m FilePath
|
:: (MonadSite m, SiteEnv m ~ App) => KeyHashid Repo -> m FilePath
|
||||||
askRepoDir sharer repo = do
|
askRepoDir repo = do
|
||||||
root <- askRepoRootDir
|
root <- askRepoRootDir
|
||||||
return $ repoDir root sharer repo
|
return $ repoDir root repo
|
||||||
|
|
905
src/Vervis/Recipient.hs
Normal file
905
src/Vervis/Recipient.hs
Normal file
|
@ -0,0 +1,905 @@
|
||||||
|
{- This file is part of Vervis.
|
||||||
|
-
|
||||||
|
- Written in 2019, 2020, 2022 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/>.
|
||||||
|
-}
|
||||||
|
|
||||||
|
-- These are for the Barbie-based generated Eq and Ord instances
|
||||||
|
{-# LANGUAGE DeriveAnyClass #-}
|
||||||
|
{-# LANGUAGE DeriveGeneric #-}
|
||||||
|
{-# LANGUAGE StandaloneDeriving #-}
|
||||||
|
{-# LANGUAGE UndecidableInstances #-}
|
||||||
|
|
||||||
|
module Vervis.Recipient
|
||||||
|
( -- * Local actors
|
||||||
|
LocalActorBy (..)
|
||||||
|
, LocalActor
|
||||||
|
, parseLocalActor
|
||||||
|
, renderLocalActor
|
||||||
|
|
||||||
|
-- * Local collections of (local and remote) actors
|
||||||
|
, LocalStageBy (..)
|
||||||
|
, LocalStage
|
||||||
|
, renderLocalStage
|
||||||
|
|
||||||
|
-- * Converting between KeyHashid, Key, Identity and Entity
|
||||||
|
, hashLocalActorPure
|
||||||
|
, getHashLocalActor
|
||||||
|
, hashLocalActor
|
||||||
|
|
||||||
|
, unhashLocalActorPure
|
||||||
|
, unhashLocalActor
|
||||||
|
, unhashLocalActorF
|
||||||
|
, unhashLocalActorM
|
||||||
|
, unhashLocalActorE
|
||||||
|
, unhashLocalActor404
|
||||||
|
|
||||||
|
, hashLocalStagePure
|
||||||
|
, getHashLocalStage
|
||||||
|
, hashLocalStage
|
||||||
|
|
||||||
|
, unhashLocalStagePure
|
||||||
|
, unhashLocalStage
|
||||||
|
, unhashLocalStageF
|
||||||
|
, unhashLocalStageM
|
||||||
|
, unhashLocalStageE
|
||||||
|
, unhashLocalStage404
|
||||||
|
|
||||||
|
-- * Local recipient set
|
||||||
|
-- ** Types
|
||||||
|
, TicketRoutes (..)
|
||||||
|
, ClothRoutes (..)
|
||||||
|
, PersonRoutes (..)
|
||||||
|
, GroupRoutes (..)
|
||||||
|
, RepoRoutes (..)
|
||||||
|
, DeckRoutes (..)
|
||||||
|
, LoomRoutes (..)
|
||||||
|
, DeckFamilyRoutes (..)
|
||||||
|
, LoomFamilyRoutes (..)
|
||||||
|
, RecipientRoutes (..)
|
||||||
|
-- ** Creating
|
||||||
|
, makeRecipientSet
|
||||||
|
, actorRecips
|
||||||
|
-- * Filtering
|
||||||
|
, localRecipSieve
|
||||||
|
, localRecipSieve'
|
||||||
|
|
||||||
|
-- * Parsing audience from a received activity
|
||||||
|
, ParsedAudience (..)
|
||||||
|
, concatRecipients
|
||||||
|
, parseAudience
|
||||||
|
|
||||||
|
-- * Creating a recipient set, supporting both local and remote recips
|
||||||
|
, Aud (..)
|
||||||
|
, collectAudience
|
||||||
|
)
|
||||||
|
where
|
||||||
|
|
||||||
|
import Control.Applicative
|
||||||
|
import Control.Monad
|
||||||
|
import Control.Monad.Trans.Except
|
||||||
|
import Control.Monad.Trans.Maybe
|
||||||
|
import Data.Barbie
|
||||||
|
import Data.Bifunctor
|
||||||
|
import Data.Either
|
||||||
|
import Data.Foldable
|
||||||
|
import Data.Functor.Classes
|
||||||
|
import Data.List ((\\))
|
||||||
|
import Data.List.NonEmpty (NonEmpty, nonEmpty)
|
||||||
|
import Data.Maybe
|
||||||
|
import Data.Semigroup
|
||||||
|
import Data.Text (Text)
|
||||||
|
import Data.These
|
||||||
|
import Data.Traversable
|
||||||
|
import GHC.Generics
|
||||||
|
import Web.Hashids
|
||||||
|
import Yesod.Core
|
||||||
|
|
||||||
|
import qualified Control.Monad.Fail as F
|
||||||
|
import qualified Data.List.NonEmpty as NE
|
||||||
|
import qualified Data.List.Ordered as LO
|
||||||
|
import qualified Data.Text as T
|
||||||
|
|
||||||
|
import Network.FedURI
|
||||||
|
import Yesod.ActivityPub
|
||||||
|
import Yesod.FedURI
|
||||||
|
import Yesod.Hashids
|
||||||
|
import Yesod.MonadSite
|
||||||
|
|
||||||
|
import qualified Web.ActivityPub as AP
|
||||||
|
|
||||||
|
import Data.List.Local
|
||||||
|
import Data.List.NonEmpty.Local
|
||||||
|
|
||||||
|
import Vervis.FedURI
|
||||||
|
import Vervis.Foundation
|
||||||
|
import Vervis.Model
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Actor and collection-of-actors types
|
||||||
|
--
|
||||||
|
-- These are the 2 kinds of local recipients. This is the starting point for
|
||||||
|
-- grouping and checking recipient lists: First parse recipient URIs into these
|
||||||
|
-- types, then you can do any further parsing and grouping.
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
data LocalActorBy f
|
||||||
|
= LocalActorPerson (f Person)
|
||||||
|
| LocalActorGroup (f Group)
|
||||||
|
| LocalActorRepo (f Repo)
|
||||||
|
| LocalActorDeck (f Deck)
|
||||||
|
| LocalActorLoom (f Loom)
|
||||||
|
deriving (Generic, FunctorB, ConstraintsB)
|
||||||
|
|
||||||
|
deriving instance AllBF Eq f LocalActorBy => Eq (LocalActorBy f)
|
||||||
|
deriving instance AllBF Ord f LocalActorBy => Ord (LocalActorBy f)
|
||||||
|
|
||||||
|
{-
|
||||||
|
instance (Eq (f Person), Eq (f Group), Eq (f Repo), Eq (f Deck), Eq (f Loom)) => Eq (LocalActorBy f) where
|
||||||
|
(==) (LocalActorPerson p) (LocalActorPerson p') = p == p'
|
||||||
|
(==) (LocalActorGroup g) (LocalActorGroup g') = g == g'
|
||||||
|
(==) (LocalActorRepo r) (LocalActorRepo r') = r == r'
|
||||||
|
(==) (LocalActorDeck d) (LocalActorDeck d') = d == d'
|
||||||
|
(==) (LocalActorLoom l) (LocalActorLoom l') = l == l'
|
||||||
|
(==) _ _ = False
|
||||||
|
|
||||||
|
instance (Ord (f Person), Ord (f Group), Ord (f Repo), Ord (f Deck), Ord (f Loom)) => Ord (LocalActorBy f) where
|
||||||
|
(<=) (LocalActorPerson p) (LocalActorPerson p') = p <= p'
|
||||||
|
(<=) (LocalActorPerson _) _ = True
|
||||||
|
|
||||||
|
(<=) (LocalActorGroup _) (LocalActorPerson _) = False
|
||||||
|
(<=) (LocalActorGroup g) (LocalActorGroup g') = g <= g'
|
||||||
|
(<=) (LocalActorGroup _) _ = True
|
||||||
|
|
||||||
|
(<=) (LocalActorGroup _) (LocalActorPerson _) = False
|
||||||
|
(<=) (LocalActorGroup g) (LocalActorGroup g') = g <= g'
|
||||||
|
(<=) (LocalActorGroup _) _ = True
|
||||||
|
-}
|
||||||
|
|
||||||
|
type LocalActor = LocalActorBy KeyHashid
|
||||||
|
|
||||||
|
parseLocalActor :: Route App -> Maybe LocalActor
|
||||||
|
parseLocalActor (PersonR pkhid) = Just $ LocalActorPerson pkhid
|
||||||
|
parseLocalActor (GroupR gkhid) = Just $ LocalActorGroup gkhid
|
||||||
|
parseLocalActor (RepoR rkhid) = Just $ LocalActorRepo rkhid
|
||||||
|
parseLocalActor (DeckR dkhid) = Just $ LocalActorDeck dkhid
|
||||||
|
parseLocalActor (LoomR lkhid) = Just $ LocalActorLoom lkhid
|
||||||
|
parseLocalActor _ = Nothing
|
||||||
|
|
||||||
|
renderLocalActor :: LocalActor -> Route App
|
||||||
|
renderLocalActor (LocalActorPerson pkhid) = PersonR pkhid
|
||||||
|
renderLocalActor (LocalActorGroup gkhid) = GroupR gkhid
|
||||||
|
renderLocalActor (LocalActorRepo rkhid) = RepoR rkhid
|
||||||
|
renderLocalActor (LocalActorDeck dkhid) = DeckR dkhid
|
||||||
|
renderLocalActor (LocalActorLoom lkhid) = LoomR lkhid
|
||||||
|
|
||||||
|
data LocalStageBy f
|
||||||
|
= LocalStagePersonFollowers (f Person)
|
||||||
|
|
||||||
|
| LocalStageRepoFollowers (f Repo)
|
||||||
|
|
||||||
|
| LocalStageDeckFollowers (f Deck)
|
||||||
|
| LocalStageTicketFollowers (f Deck) (f TicketDeck)
|
||||||
|
|
||||||
|
| LocalStageLoomFollowers (f Loom)
|
||||||
|
| LocalStageClothFollowers (f Loom) (f TicketLoom)
|
||||||
|
deriving (Generic, FunctorB, ConstraintsB)
|
||||||
|
|
||||||
|
deriving instance AllBF Eq f LocalStageBy => Eq (LocalStageBy f)
|
||||||
|
deriving instance AllBF Ord f LocalStageBy => Ord (LocalStageBy f)
|
||||||
|
|
||||||
|
type LocalStage = LocalStageBy KeyHashid
|
||||||
|
|
||||||
|
parseLocalStage :: Route App -> Maybe LocalStage
|
||||||
|
parseLocalStage (PersonFollowersR pkhid) =
|
||||||
|
Just $ LocalStagePersonFollowers pkhid
|
||||||
|
parseLocalStage (RepoFollowersR rkhid) =
|
||||||
|
Just $ LocalStageRepoFollowers rkhid
|
||||||
|
parseLocalStage (DeckFollowersR dkhid) =
|
||||||
|
Just $ LocalStageDeckFollowers dkhid
|
||||||
|
parseLocalStage (TicketFollowersR dkhid ltkhid) =
|
||||||
|
Just $ LocalStageTicketFollowers dkhid ltkhid
|
||||||
|
parseLocalStage (LoomFollowersR lkhid) =
|
||||||
|
Just $ LocalStageLoomFollowers lkhid
|
||||||
|
parseLocalStage (ClothFollowersR lkhid ltkhid) =
|
||||||
|
Just $ LocalStageClothFollowers lkhid ltkhid
|
||||||
|
parseLocalStage _ = Nothing
|
||||||
|
|
||||||
|
renderLocalStage :: LocalStage -> Route App
|
||||||
|
renderLocalStage (LocalStagePersonFollowers pkhid) =
|
||||||
|
PersonFollowersR pkhid
|
||||||
|
renderLocalStage (LocalStageRepoFollowers rkhid) =
|
||||||
|
RepoFollowersR rkhid
|
||||||
|
renderLocalStage (LocalStageDeckFollowers dkhid) =
|
||||||
|
DeckFollowersR dkhid
|
||||||
|
renderLocalStage (LocalStageTicketFollowers dkhid ltkhid) =
|
||||||
|
TicketFollowersR dkhid ltkhid
|
||||||
|
renderLocalStage (LocalStageLoomFollowers lkhid) =
|
||||||
|
LoomFollowersR lkhid
|
||||||
|
renderLocalStage (LocalStageClothFollowers lkhid ltkhid) =
|
||||||
|
ClothFollowersR lkhid ltkhid
|
||||||
|
|
||||||
|
parseLocalRecipient :: Route App -> Maybe (Either LocalActor LocalStage)
|
||||||
|
parseLocalRecipient r =
|
||||||
|
Left <$> parseLocalActor r <|> Right <$> parseLocalStage r
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Converting between KeyHashid, Key, Identity and Entity
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
hashLocalActorPure
|
||||||
|
:: HashidsContext -> LocalActorBy Key -> LocalActorBy KeyHashid
|
||||||
|
hashLocalActorPure ctx = f
|
||||||
|
where
|
||||||
|
f (LocalActorPerson p) = LocalActorPerson $ encodeKeyHashidPure ctx p
|
||||||
|
f (LocalActorGroup g) = LocalActorGroup $ encodeKeyHashidPure ctx g
|
||||||
|
f (LocalActorRepo r) = LocalActorRepo $ encodeKeyHashidPure ctx r
|
||||||
|
f (LocalActorDeck d) = LocalActorDeck $ encodeKeyHashidPure ctx d
|
||||||
|
f (LocalActorLoom l) = LocalActorLoom $ encodeKeyHashidPure ctx l
|
||||||
|
|
||||||
|
getHashLocalActor
|
||||||
|
:: (MonadSite m, YesodHashids (SiteEnv m))
|
||||||
|
=> m (LocalActorBy Key -> LocalActorBy KeyHashid)
|
||||||
|
getHashLocalActor = do
|
||||||
|
ctx <- asksSite siteHashidsContext
|
||||||
|
return $ hashLocalActorPure ctx
|
||||||
|
|
||||||
|
hashLocalActor
|
||||||
|
:: (MonadSite m, YesodHashids (SiteEnv m))
|
||||||
|
=> LocalActorBy Key -> m (LocalActorBy KeyHashid)
|
||||||
|
hashLocalActor actor = do
|
||||||
|
hash <- getHashLocalActor
|
||||||
|
return $ hash actor
|
||||||
|
|
||||||
|
unhashLocalActorPure
|
||||||
|
:: HashidsContext -> LocalActorBy KeyHashid -> Maybe (LocalActorBy Key)
|
||||||
|
unhashLocalActorPure ctx = f
|
||||||
|
where
|
||||||
|
f (LocalActorPerson p) = LocalActorPerson <$> decodeKeyHashidPure ctx p
|
||||||
|
f (LocalActorGroup g) = LocalActorGroup <$> decodeKeyHashidPure ctx g
|
||||||
|
f (LocalActorRepo r) = LocalActorRepo <$> decodeKeyHashidPure ctx r
|
||||||
|
f (LocalActorDeck d) = LocalActorDeck <$> decodeKeyHashidPure ctx d
|
||||||
|
f (LocalActorLoom l) = LocalActorLoom <$> decodeKeyHashidPure ctx l
|
||||||
|
|
||||||
|
unhashLocalActor
|
||||||
|
:: (MonadSite m, YesodHashids (SiteEnv m))
|
||||||
|
=> LocalActorBy KeyHashid -> m (Maybe (LocalActorBy Key))
|
||||||
|
unhashLocalActor actor = do
|
||||||
|
ctx <- asksSite siteHashidsContext
|
||||||
|
return $ unhashLocalActorPure ctx actor
|
||||||
|
|
||||||
|
unhashLocalActorF
|
||||||
|
:: (F.MonadFail m, MonadSite m, YesodHashids (SiteEnv m))
|
||||||
|
=> LocalActorBy KeyHashid -> String -> m (LocalActorBy Key)
|
||||||
|
unhashLocalActorF actor e = maybe (F.fail e) return =<< unhashLocalActor actor
|
||||||
|
|
||||||
|
unhashLocalActorM
|
||||||
|
:: (MonadSite m, YesodHashids (SiteEnv m))
|
||||||
|
=> LocalActorBy KeyHashid -> MaybeT m (LocalActorBy Key)
|
||||||
|
unhashLocalActorM = MaybeT . unhashLocalActor
|
||||||
|
|
||||||
|
unhashLocalActorE
|
||||||
|
:: (MonadSite m, YesodHashids (SiteEnv m))
|
||||||
|
=> LocalActorBy KeyHashid -> e -> ExceptT e m (LocalActorBy Key)
|
||||||
|
unhashLocalActorE actor e =
|
||||||
|
ExceptT $ maybe (Left e) Right <$> unhashLocalActor actor
|
||||||
|
|
||||||
|
unhashLocalActor404
|
||||||
|
:: ( MonadSite m
|
||||||
|
, MonadHandler m
|
||||||
|
, HandlerSite m ~ SiteEnv m
|
||||||
|
, YesodHashids (HandlerSite m)
|
||||||
|
)
|
||||||
|
=> LocalActorBy KeyHashid
|
||||||
|
-> m (LocalActorBy Key)
|
||||||
|
unhashLocalActor404 actor = maybe notFound return =<< unhashLocalActor actor
|
||||||
|
|
||||||
|
hashLocalStagePure
|
||||||
|
:: HashidsContext -> LocalStageBy Key -> LocalStageBy KeyHashid
|
||||||
|
hashLocalStagePure ctx = f
|
||||||
|
where
|
||||||
|
f (LocalStagePersonFollowers p) =
|
||||||
|
LocalStagePersonFollowers $ encodeKeyHashidPure ctx p
|
||||||
|
f (LocalStageRepoFollowers r) =
|
||||||
|
LocalStageRepoFollowers $ encodeKeyHashidPure ctx r
|
||||||
|
f (LocalStageDeckFollowers d) =
|
||||||
|
LocalStageDeckFollowers $ encodeKeyHashidPure ctx d
|
||||||
|
f (LocalStageTicketFollowers d t) =
|
||||||
|
LocalStageTicketFollowers
|
||||||
|
(encodeKeyHashidPure ctx d)
|
||||||
|
(encodeKeyHashidPure ctx t)
|
||||||
|
f (LocalStageLoomFollowers l) =
|
||||||
|
LocalStageLoomFollowers $ encodeKeyHashidPure ctx l
|
||||||
|
f (LocalStageClothFollowers l c) =
|
||||||
|
LocalStageClothFollowers
|
||||||
|
(encodeKeyHashidPure ctx l)
|
||||||
|
(encodeKeyHashidPure ctx c)
|
||||||
|
|
||||||
|
getHashLocalStage
|
||||||
|
:: (MonadSite m, YesodHashids (SiteEnv m))
|
||||||
|
=> m (LocalStageBy Key -> LocalStageBy KeyHashid)
|
||||||
|
getHashLocalStage = do
|
||||||
|
ctx <- asksSite siteHashidsContext
|
||||||
|
return $ hashLocalStagePure ctx
|
||||||
|
|
||||||
|
hashLocalStage
|
||||||
|
:: (MonadSite m, YesodHashids (SiteEnv m))
|
||||||
|
=> LocalStageBy Key -> m (LocalStageBy KeyHashid)
|
||||||
|
hashLocalStage stage = do
|
||||||
|
hash <- getHashLocalStage
|
||||||
|
return $ hash stage
|
||||||
|
|
||||||
|
unhashLocalStagePure
|
||||||
|
:: HashidsContext -> LocalStageBy KeyHashid -> Maybe (LocalStageBy Key)
|
||||||
|
unhashLocalStagePure ctx = f
|
||||||
|
where
|
||||||
|
f (LocalStagePersonFollowers p) =
|
||||||
|
LocalStagePersonFollowers <$> decodeKeyHashidPure ctx p
|
||||||
|
f (LocalStageRepoFollowers r) =
|
||||||
|
LocalStageRepoFollowers <$> decodeKeyHashidPure ctx r
|
||||||
|
f (LocalStageDeckFollowers d) =
|
||||||
|
LocalStageDeckFollowers <$> decodeKeyHashidPure ctx d
|
||||||
|
f (LocalStageTicketFollowers d t) =
|
||||||
|
LocalStageTicketFollowers
|
||||||
|
<$> decodeKeyHashidPure ctx d
|
||||||
|
<*> decodeKeyHashidPure ctx t
|
||||||
|
f (LocalStageLoomFollowers l) =
|
||||||
|
LocalStageLoomFollowers <$> decodeKeyHashidPure ctx l
|
||||||
|
f (LocalStageClothFollowers l c) =
|
||||||
|
LocalStageClothFollowers
|
||||||
|
<$> decodeKeyHashidPure ctx l
|
||||||
|
<*> decodeKeyHashidPure ctx c
|
||||||
|
|
||||||
|
unhashLocalStage
|
||||||
|
:: (MonadSite m, YesodHashids (SiteEnv m))
|
||||||
|
=> LocalStageBy KeyHashid -> m (Maybe (LocalStageBy Key))
|
||||||
|
unhashLocalStage stage = do
|
||||||
|
ctx <- asksSite siteHashidsContext
|
||||||
|
return $ unhashLocalStagePure ctx stage
|
||||||
|
|
||||||
|
unhashLocalStageF
|
||||||
|
:: (F.MonadFail m, MonadSite m, YesodHashids (SiteEnv m))
|
||||||
|
=> LocalStageBy KeyHashid -> String -> m (LocalStageBy Key)
|
||||||
|
unhashLocalStageF stage e = maybe (F.fail e) return =<< unhashLocalStage stage
|
||||||
|
|
||||||
|
unhashLocalStageM
|
||||||
|
:: (MonadSite m, YesodHashids (SiteEnv m))
|
||||||
|
=> LocalStageBy KeyHashid -> MaybeT m (LocalStageBy Key)
|
||||||
|
unhashLocalStageM = MaybeT . unhashLocalStage
|
||||||
|
|
||||||
|
unhashLocalStageE
|
||||||
|
:: (MonadSite m, YesodHashids (SiteEnv m))
|
||||||
|
=> LocalStageBy KeyHashid -> e -> ExceptT e m (LocalStageBy Key)
|
||||||
|
unhashLocalStageE stage e =
|
||||||
|
ExceptT $ maybe (Left e) Right <$> unhashLocalStage stage
|
||||||
|
|
||||||
|
unhashLocalStage404
|
||||||
|
:: ( MonadSite m
|
||||||
|
, MonadHandler m
|
||||||
|
, HandlerSite m ~ SiteEnv m
|
||||||
|
, YesodHashids (HandlerSite m)
|
||||||
|
)
|
||||||
|
=> LocalStageBy KeyHashid
|
||||||
|
-> m (LocalStageBy Key)
|
||||||
|
unhashLocalStage404 stage = maybe notFound return =<< unhashLocalStage stage
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Intermediate recipient types
|
||||||
|
--
|
||||||
|
-- These are here just to help with grouping recipients. From this
|
||||||
|
-- representation it's easy to group recipients into a form that is friendly to
|
||||||
|
-- the code that fetches the actual recipients from the DB.
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
data LeafTicket = LeafTicketFollowers deriving (Eq, Ord)
|
||||||
|
|
||||||
|
data LeafCloth = LeafClothFollowers deriving (Eq, Ord)
|
||||||
|
|
||||||
|
data LeafPerson = LeafPerson | LeafPersonFollowers deriving (Eq, Ord)
|
||||||
|
|
||||||
|
data LeafGroup = LeafGroup deriving (Eq, Ord)
|
||||||
|
|
||||||
|
data LeafRepo = LeafRepo | LeafRepoFollowers deriving (Eq, Ord)
|
||||||
|
|
||||||
|
data LeafDeck = LeafDeck | LeafDeckFollowers deriving (Eq, Ord)
|
||||||
|
|
||||||
|
data LeafLoom = LeafLoom | LeafLoomFollowers deriving (Eq, Ord)
|
||||||
|
|
||||||
|
data PieceDeck
|
||||||
|
= PieceDeck LeafDeck
|
||||||
|
| PieceTicket (KeyHashid TicketDeck) LeafTicket
|
||||||
|
deriving (Eq, Ord)
|
||||||
|
|
||||||
|
data PieceLoom
|
||||||
|
= PieceLoom LeafLoom
|
||||||
|
| PieceCloth (KeyHashid TicketLoom) LeafCloth
|
||||||
|
deriving (Eq, Ord)
|
||||||
|
|
||||||
|
data LocalRecipient
|
||||||
|
= RecipPerson (KeyHashid Person) LeafPerson
|
||||||
|
| RecipGroup (KeyHashid Group) LeafGroup
|
||||||
|
| RecipRepo (KeyHashid Repo) LeafRepo
|
||||||
|
| RecipDeck (KeyHashid Deck) PieceDeck
|
||||||
|
| RecipLoom (KeyHashid Loom) PieceLoom
|
||||||
|
deriving (Eq, Ord)
|
||||||
|
|
||||||
|
recipientFromActor :: LocalActor -> LocalRecipient
|
||||||
|
recipientFromActor (LocalActorPerson pkhid) =
|
||||||
|
RecipPerson pkhid LeafPerson
|
||||||
|
recipientFromActor (LocalActorGroup gkhid) =
|
||||||
|
RecipGroup gkhid LeafGroup
|
||||||
|
recipientFromActor (LocalActorRepo rkhid) =
|
||||||
|
RecipRepo rkhid LeafRepo
|
||||||
|
recipientFromActor (LocalActorDeck dkhid) =
|
||||||
|
RecipDeck dkhid $ PieceDeck LeafDeck
|
||||||
|
recipientFromActor (LocalActorLoom lkhid) =
|
||||||
|
RecipLoom lkhid $ PieceLoom LeafLoom
|
||||||
|
|
||||||
|
recipientFromStage :: LocalStage -> LocalRecipient
|
||||||
|
recipientFromStage (LocalStagePersonFollowers pkhid) =
|
||||||
|
RecipPerson pkhid LeafPersonFollowers
|
||||||
|
recipientFromStage (LocalStageRepoFollowers rkhid) =
|
||||||
|
RecipRepo rkhid LeafRepoFollowers
|
||||||
|
recipientFromStage (LocalStageDeckFollowers dkhid) =
|
||||||
|
RecipDeck dkhid $ PieceDeck LeafDeckFollowers
|
||||||
|
recipientFromStage (LocalStageTicketFollowers dkhid ltkhid) =
|
||||||
|
RecipDeck dkhid $ PieceTicket ltkhid LeafTicketFollowers
|
||||||
|
recipientFromStage (LocalStageLoomFollowers lkhid) =
|
||||||
|
RecipLoom lkhid $ PieceLoom LeafLoomFollowers
|
||||||
|
recipientFromStage (LocalStageClothFollowers lkhid ltkhid) =
|
||||||
|
RecipLoom lkhid $ PieceCloth ltkhid LeafClothFollowers
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Recipient set types
|
||||||
|
--
|
||||||
|
-- These types represent a set of recipients grouped by the variable components
|
||||||
|
-- of their routes. It's convenient to use when looking for the recipients in
|
||||||
|
-- the DB, and easy to manipulate and check the recipient list in terms of app
|
||||||
|
-- logic rather than plain lists of routes.
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
data TicketRoutes = TicketRoutes
|
||||||
|
{ routeTicketFollowers :: Bool
|
||||||
|
}
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
data ClothRoutes = ClothRoutes
|
||||||
|
{ routeClothFollowers :: Bool
|
||||||
|
}
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
data PersonRoutes = PersonRoutes
|
||||||
|
{ routePerson :: Bool
|
||||||
|
, routePersonFollowers :: Bool
|
||||||
|
}
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
data GroupRoutes = GroupRoutes
|
||||||
|
{ routeGroup :: Bool
|
||||||
|
}
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
data RepoRoutes = RepoRoutes
|
||||||
|
{ routeRepo :: Bool
|
||||||
|
, routeRepoFollowers :: Bool
|
||||||
|
}
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
data DeckRoutes = DeckRoutes
|
||||||
|
{ routeDeck :: Bool
|
||||||
|
, routeDeckFollowers :: Bool
|
||||||
|
}
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
data LoomRoutes = LoomRoutes
|
||||||
|
{ routeLoom :: Bool
|
||||||
|
, routeLoomFollowers :: Bool
|
||||||
|
}
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
data DeckFamilyRoutes = DeckFamilyRoutes
|
||||||
|
{ familyDeck :: DeckRoutes
|
||||||
|
, familyTickets :: [(KeyHashid TicketDeck, TicketRoutes)]
|
||||||
|
}
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
data LoomFamilyRoutes = LoomFamilyRoutes
|
||||||
|
{ familyLoom :: LoomRoutes
|
||||||
|
, familyCloths :: [(KeyHashid TicketLoom, ClothRoutes)]
|
||||||
|
}
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
data RecipientRoutes = RecipientRoutes
|
||||||
|
{ recipPeople :: [(KeyHashid Person, PersonRoutes)]
|
||||||
|
, recipGroups :: [(KeyHashid Group , GroupRoutes)]
|
||||||
|
, recipRepos :: [(KeyHashid Repo , RepoRoutes)]
|
||||||
|
, recipDecks :: [(KeyHashid Deck , DeckFamilyRoutes)]
|
||||||
|
, recipLooms :: [(KeyHashid Loom , LoomFamilyRoutes)]
|
||||||
|
}
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
groupLocalRecipients :: [LocalRecipient] -> RecipientRoutes
|
||||||
|
groupLocalRecipients = organize . partitionByActor
|
||||||
|
where
|
||||||
|
partitionByActor
|
||||||
|
:: [LocalRecipient]
|
||||||
|
-> ( [(KeyHashid Person, LeafPerson)]
|
||||||
|
, [(KeyHashid Group, LeafGroup)]
|
||||||
|
, [(KeyHashid Repo, LeafRepo)]
|
||||||
|
, [(KeyHashid Deck, PieceDeck)]
|
||||||
|
, [(KeyHashid Loom, PieceLoom)]
|
||||||
|
)
|
||||||
|
partitionByActor = foldl' f ([], [], [], [], [])
|
||||||
|
where
|
||||||
|
f (p, g, r, d, l) (RecipPerson pkhid pleaf) =
|
||||||
|
((pkhid, pleaf) : p, g, r, d, l)
|
||||||
|
f (p, g, r, d, l) (RecipGroup gkhid gleaf) =
|
||||||
|
(p, (gkhid, gleaf) : g, r, d, l)
|
||||||
|
f (p, g, r, d, l) (RecipRepo rkhid rleaf) =
|
||||||
|
(p, g, (rkhid, rleaf) : r, d, l)
|
||||||
|
f (p, g, r, d, l) (RecipDeck dkhid dpiece) =
|
||||||
|
(p, g, r, (dkhid, dpiece) : d, l)
|
||||||
|
f (p, g, r, d, l) (RecipLoom lkhid lpiece) =
|
||||||
|
(p, g, r, d, (lkhid, lpiece) : l)
|
||||||
|
|
||||||
|
organize
|
||||||
|
:: ( [(KeyHashid Person, LeafPerson)]
|
||||||
|
, [(KeyHashid Group, LeafGroup)]
|
||||||
|
, [(KeyHashid Repo, LeafRepo)]
|
||||||
|
, [(KeyHashid Deck, PieceDeck)]
|
||||||
|
, [(KeyHashid Loom, PieceLoom)]
|
||||||
|
)
|
||||||
|
-> RecipientRoutes
|
||||||
|
organize (p, g, r, d, l) = RecipientRoutes
|
||||||
|
{ recipPeople =
|
||||||
|
map (second $ foldr orLP $ PersonRoutes False False) $ groupByKeySort p
|
||||||
|
, recipGroups =
|
||||||
|
map (second $ foldr orLG $ GroupRoutes False) $ groupByKeySort g
|
||||||
|
, recipRepos =
|
||||||
|
map (second $ foldr orLR $ RepoRoutes False False) $ groupByKeySort r
|
||||||
|
, recipDecks =
|
||||||
|
map (second
|
||||||
|
$ uncurry DeckFamilyRoutes
|
||||||
|
. bimap
|
||||||
|
(foldr orLD $ DeckRoutes False False)
|
||||||
|
( map (second $ foldr orLT $ TicketRoutes False)
|
||||||
|
. groupByKey
|
||||||
|
)
|
||||||
|
. partitionEithers . NE.toList . NE.map pd2either
|
||||||
|
) $
|
||||||
|
groupByKeySort d
|
||||||
|
, recipLooms =
|
||||||
|
map (second
|
||||||
|
$ uncurry LoomFamilyRoutes
|
||||||
|
. bimap
|
||||||
|
(foldr orLL $ LoomRoutes False False)
|
||||||
|
( map (second $ foldr orLC $ ClothRoutes False)
|
||||||
|
. groupByKey
|
||||||
|
)
|
||||||
|
. partitionEithers . NE.toList . NE.map pl2either
|
||||||
|
) $
|
||||||
|
groupByKeySort l
|
||||||
|
}
|
||||||
|
where
|
||||||
|
groupByKey :: (Foldable f, Eq a) => f (a, b) -> [(a, NonEmpty b)]
|
||||||
|
groupByKey = groupWithExtract fst snd
|
||||||
|
|
||||||
|
groupByKeySort :: Ord a => [(a, b)] -> [(a, NonEmpty b)]
|
||||||
|
groupByKeySort = groupAllExtract fst snd
|
||||||
|
|
||||||
|
orLP :: LeafPerson -> PersonRoutes -> PersonRoutes
|
||||||
|
orLP _ pr@(PersonRoutes True True) = pr
|
||||||
|
orLP LeafPerson pr@(PersonRoutes _ _) = pr { routePerson = True }
|
||||||
|
orLP LeafPersonFollowers pr@(PersonRoutes _ _) = pr { routePersonFollowers = True }
|
||||||
|
|
||||||
|
orLG :: LeafGroup -> GroupRoutes -> GroupRoutes
|
||||||
|
orLG _ gr@(GroupRoutes True) = gr
|
||||||
|
orLG LeafGroup gr@(GroupRoutes _) = gr { routeGroup = True }
|
||||||
|
|
||||||
|
orLR :: LeafRepo -> RepoRoutes -> RepoRoutes
|
||||||
|
orLR _ rr@(RepoRoutes True True) = rr
|
||||||
|
orLR LeafRepo rr@(RepoRoutes _ _) = rr { routeRepo = True }
|
||||||
|
orLR LeafRepoFollowers rr@(RepoRoutes _ _) = rr { routeRepoFollowers = True }
|
||||||
|
|
||||||
|
orLD :: LeafDeck -> DeckRoutes -> DeckRoutes
|
||||||
|
orLD _ dr@(DeckRoutes True True) = dr
|
||||||
|
orLD LeafDeck dr@(DeckRoutes _ _) = dr { routeDeck = True }
|
||||||
|
orLD LeafDeckFollowers dr@(DeckRoutes _ _) = dr { routeDeckFollowers = True }
|
||||||
|
|
||||||
|
orLL :: LeafLoom -> LoomRoutes -> LoomRoutes
|
||||||
|
orLL _ lr@(LoomRoutes True True) = lr
|
||||||
|
orLL LeafLoom lr@(LoomRoutes _ _) = lr { routeLoom = True }
|
||||||
|
orLL LeafLoomFollowers lr@(LoomRoutes _ _) = lr { routeLoomFollowers = True }
|
||||||
|
|
||||||
|
orLT :: LeafTicket -> TicketRoutes -> TicketRoutes
|
||||||
|
orLT _ tr@(TicketRoutes True) = tr
|
||||||
|
orLT LeafTicketFollowers tr@(TicketRoutes _) = tr { routeTicketFollowers = True }
|
||||||
|
|
||||||
|
orLC :: LeafCloth -> ClothRoutes -> ClothRoutes
|
||||||
|
orLC _ cr@(ClothRoutes True) = cr
|
||||||
|
orLC LeafClothFollowers cr@(ClothRoutes _) = cr { routeClothFollowers = True }
|
||||||
|
|
||||||
|
pd2either :: PieceDeck -> Either LeafDeck (KeyHashid TicketDeck, LeafTicket)
|
||||||
|
pd2either (PieceDeck ld) = Left ld
|
||||||
|
pd2either (PieceTicket ltkhid lt) = Right (ltkhid, lt)
|
||||||
|
|
||||||
|
pl2either :: PieceLoom -> Either LeafLoom (KeyHashid TicketLoom, LeafCloth)
|
||||||
|
pl2either (PieceLoom ll) = Left ll
|
||||||
|
pl2either (PieceCloth ltkhid ll) = Right (ltkhid, ll)
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Parse URIs into a grouped recipient set
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
makeRecipientSet :: [LocalActor] -> [LocalStage] -> RecipientRoutes
|
||||||
|
makeRecipientSet actors stages =
|
||||||
|
groupLocalRecipients $
|
||||||
|
map recipientFromActor actors ++ map recipientFromStage stages
|
||||||
|
|
||||||
|
actorIsMember :: LocalActor -> RecipientRoutes -> Bool
|
||||||
|
actorIsMember (LocalActorPerson pkhid) routes =
|
||||||
|
case lookup pkhid $ recipPeople routes of
|
||||||
|
Just p -> routePerson p
|
||||||
|
Nothing -> False
|
||||||
|
actorIsMember (LocalActorGroup gkhid) routes =
|
||||||
|
case lookup gkhid $ recipGroups routes of
|
||||||
|
Just g -> routeGroup g
|
||||||
|
Nothing -> False
|
||||||
|
actorIsMember (LocalActorRepo rkhid) routes =
|
||||||
|
case lookup rkhid $ recipRepos routes of
|
||||||
|
Just r -> routeRepo r
|
||||||
|
Nothing -> False
|
||||||
|
actorIsMember (LocalActorDeck dkhid) routes =
|
||||||
|
case lookup dkhid $ recipDecks routes of
|
||||||
|
Just d -> routeDeck $ familyDeck d
|
||||||
|
Nothing -> False
|
||||||
|
actorIsMember (LocalActorLoom lkhid) routes =
|
||||||
|
case lookup lkhid $ recipLooms routes of
|
||||||
|
Just l -> routeLoom $ familyLoom l
|
||||||
|
Nothing -> False
|
||||||
|
|
||||||
|
actorRecips :: LocalActor -> RecipientRoutes
|
||||||
|
actorRecips = groupLocalRecipients . (: []) . recipientFromActor
|
||||||
|
|
||||||
|
localRecipSieve
|
||||||
|
:: RecipientRoutes -> Bool -> RecipientRoutes -> RecipientRoutes
|
||||||
|
localRecipSieve sieve allowActors =
|
||||||
|
localRecipSieve' sieve allowActors allowActors
|
||||||
|
|
||||||
|
localRecipSieve'
|
||||||
|
:: RecipientRoutes
|
||||||
|
-> Bool
|
||||||
|
-> Bool
|
||||||
|
-> RecipientRoutes
|
||||||
|
-> RecipientRoutes
|
||||||
|
localRecipSieve' sieve allowPeople allowOthers routes = RecipientRoutes
|
||||||
|
{ recipPeople = applySieve' applyPerson recipPeople
|
||||||
|
, recipGroups = applySieve' applyGroup recipGroups
|
||||||
|
, recipRepos = applySieve' applyRepo recipRepos
|
||||||
|
, recipDecks = applySieve' applyDeck recipDecks
|
||||||
|
, recipLooms = applySieve' applyLoom recipLooms
|
||||||
|
}
|
||||||
|
where
|
||||||
|
applySieve
|
||||||
|
:: ( KeyHashid record
|
||||||
|
-> These routes routes
|
||||||
|
-> Maybe (KeyHashid record, routes)
|
||||||
|
)
|
||||||
|
-> [(KeyHashid record, routes)]
|
||||||
|
-> [(KeyHashid record, routes)]
|
||||||
|
-> [(KeyHashid record, routes)]
|
||||||
|
applySieve merge sieveList routeList =
|
||||||
|
mapMaybe (uncurry merge) $ sortAlign sieveList routeList
|
||||||
|
|
||||||
|
applySieve'
|
||||||
|
:: ( KeyHashid record
|
||||||
|
-> These routes routes
|
||||||
|
-> Maybe (KeyHashid record, routes)
|
||||||
|
)
|
||||||
|
-> (RecipientRoutes -> [(KeyHashid record, routes)])
|
||||||
|
-> [(KeyHashid record, routes)]
|
||||||
|
applySieve' merge field = applySieve merge (field sieve) (field routes)
|
||||||
|
|
||||||
|
applyPerson _ (This _) = Nothing
|
||||||
|
applyPerson pkhid (That p) =
|
||||||
|
if allowPeople && routePerson p
|
||||||
|
then Just (pkhid, PersonRoutes True False)
|
||||||
|
else Nothing
|
||||||
|
applyPerson pkhid (These (PersonRoutes p' pf') (PersonRoutes p pf)) =
|
||||||
|
let merged = PersonRoutes (p && (p' || allowPeople)) (pf && pf')
|
||||||
|
in if merged == PersonRoutes False False
|
||||||
|
then Nothing
|
||||||
|
else Just (pkhid, merged)
|
||||||
|
|
||||||
|
applyGroup _ (This _) = Nothing
|
||||||
|
applyGroup gkhid (That g) =
|
||||||
|
if allowOthers && routeGroup g
|
||||||
|
then Just (gkhid, GroupRoutes True)
|
||||||
|
else Nothing
|
||||||
|
applyGroup gkhid (These (GroupRoutes g') (GroupRoutes g)) =
|
||||||
|
let merged = GroupRoutes (g && (g' || allowOthers))
|
||||||
|
in if merged == GroupRoutes False
|
||||||
|
then Nothing
|
||||||
|
else Just (gkhid, merged)
|
||||||
|
|
||||||
|
applyRepo _ (This _) = Nothing
|
||||||
|
applyRepo rkhid (That r) =
|
||||||
|
if allowOthers && routeRepo r
|
||||||
|
then Just (rkhid, RepoRoutes True False)
|
||||||
|
else Nothing
|
||||||
|
applyRepo rkhid (These (RepoRoutes r' rf') (RepoRoutes r rf)) =
|
||||||
|
let merged = RepoRoutes (r && (r' || allowOthers)) (rf && rf')
|
||||||
|
in if merged == RepoRoutes False False
|
||||||
|
then Nothing
|
||||||
|
else Just (rkhid, merged)
|
||||||
|
|
||||||
|
applyDeck _ (This _) = Nothing
|
||||||
|
applyDeck dkhid (That d) =
|
||||||
|
if allowOthers && routeDeck (familyDeck d)
|
||||||
|
then Just (dkhid, DeckFamilyRoutes (DeckRoutes True False) [])
|
||||||
|
else Nothing
|
||||||
|
applyDeck
|
||||||
|
dkhid
|
||||||
|
(These
|
||||||
|
(DeckFamilyRoutes (DeckRoutes d' df') t')
|
||||||
|
(DeckFamilyRoutes (DeckRoutes d df) t)
|
||||||
|
) =
|
||||||
|
let deck = DeckRoutes (d && (d' || allowOthers)) (df && df')
|
||||||
|
tickets = applySieve applyTicket t' t
|
||||||
|
where
|
||||||
|
applyTicket ltkhid (These (TicketRoutes tf') (TicketRoutes tf)) =
|
||||||
|
let merged = TicketRoutes (tf && tf')
|
||||||
|
in if merged == TicketRoutes False
|
||||||
|
then Nothing
|
||||||
|
else Just (ltkhid, merged)
|
||||||
|
applyTicket _ _ = Nothing
|
||||||
|
in if deck == DeckRoutes False False && null tickets
|
||||||
|
then Nothing
|
||||||
|
else Just (dkhid, DeckFamilyRoutes deck tickets)
|
||||||
|
|
||||||
|
applyLoom _ (This _) = Nothing
|
||||||
|
applyLoom lkhid (That d) =
|
||||||
|
if allowOthers && routeLoom (familyLoom d)
|
||||||
|
then Just (lkhid, LoomFamilyRoutes (LoomRoutes True False) [])
|
||||||
|
else Nothing
|
||||||
|
applyLoom
|
||||||
|
lkhid
|
||||||
|
(These
|
||||||
|
(LoomFamilyRoutes (LoomRoutes l' lf') c')
|
||||||
|
(LoomFamilyRoutes (LoomRoutes l lf) c)
|
||||||
|
) =
|
||||||
|
let loom = LoomRoutes (l && (l' || allowOthers)) (lf && lf')
|
||||||
|
cloths = applySieve applyCloth c' c
|
||||||
|
where
|
||||||
|
applyCloth ltkhid (These (ClothRoutes cf') (ClothRoutes cf)) =
|
||||||
|
let merged = ClothRoutes (cf && cf')
|
||||||
|
in if merged == ClothRoutes False
|
||||||
|
then Nothing
|
||||||
|
else Just (ltkhid, merged)
|
||||||
|
applyCloth _ _ = Nothing
|
||||||
|
in if loom == LoomRoutes False False && null cloths
|
||||||
|
then Nothing
|
||||||
|
else Just (lkhid, LoomFamilyRoutes loom cloths)
|
||||||
|
|
||||||
|
data ParsedAudience u = ParsedAudience
|
||||||
|
{ paudLocalRecips :: RecipientRoutes
|
||||||
|
, paudRemoteActors :: [(Authority u, NonEmpty LocalURI)]
|
||||||
|
, paudBlinded :: AP.Audience u
|
||||||
|
, paudFwdHosts :: [Authority u]
|
||||||
|
}
|
||||||
|
|
||||||
|
concatRecipients :: AP.Audience u -> [ObjURI u]
|
||||||
|
concatRecipients (AP.Audience to bto cc bcc gen _) =
|
||||||
|
concat [to, bto, cc, bcc, gen]
|
||||||
|
|
||||||
|
parseRecipients
|
||||||
|
:: (MonadSite m, SiteEnv m ~ App)
|
||||||
|
=> NonEmpty FedURI
|
||||||
|
-> ExceptT Text m (RecipientRoutes, [FedURI])
|
||||||
|
parseRecipients recips = do
|
||||||
|
hLocal <- asksSite siteInstanceHost
|
||||||
|
let (locals, remotes) = splitRecipients hLocal recips
|
||||||
|
(lusInvalid, routesInvalid, localsSet) = parseLocalRecipients locals
|
||||||
|
unless (null lusInvalid) $
|
||||||
|
throwE $
|
||||||
|
"Local recipients are invalid routes: " <>
|
||||||
|
T.pack (show $ map (renderObjURI . ObjURI hLocal) lusInvalid)
|
||||||
|
unless (null routesInvalid) $ do
|
||||||
|
renderUrl <- askUrlRender
|
||||||
|
throwE $
|
||||||
|
"Local recipients are non-recipient routes: " <>
|
||||||
|
T.pack (show $ map renderUrl routesInvalid)
|
||||||
|
return (localsSet, remotes)
|
||||||
|
where
|
||||||
|
splitRecipients :: Host -> NonEmpty FedURI -> ([LocalURI], [FedURI])
|
||||||
|
splitRecipients home recips =
|
||||||
|
let (local, remote) = NE.partition ((== home) . objUriAuthority) recips
|
||||||
|
in (map objUriLocal local, remote)
|
||||||
|
|
||||||
|
parseLocalRecipients
|
||||||
|
:: [LocalURI] -> ([LocalURI], [Route App], RecipientRoutes)
|
||||||
|
parseLocalRecipients lus =
|
||||||
|
let (lusInvalid, routes) = partitionEithers $ map parseRoute lus
|
||||||
|
(routesInvalid, recips) = partitionEithers $ map parseRecip routes
|
||||||
|
(actors, stages) = partitionEithers recips
|
||||||
|
grouped =
|
||||||
|
map recipientFromActor actors ++ map recipientFromStage stages
|
||||||
|
in (lusInvalid, routesInvalid, groupLocalRecipients grouped)
|
||||||
|
where
|
||||||
|
parseRoute lu =
|
||||||
|
case decodeRouteLocal lu of
|
||||||
|
Nothing -> Left lu
|
||||||
|
Just route -> Right route
|
||||||
|
parseRecip route =
|
||||||
|
case parseLocalRecipient route of
|
||||||
|
Nothing -> Left route
|
||||||
|
Just recip -> Right recip
|
||||||
|
|
||||||
|
parseAudience
|
||||||
|
:: (MonadSite m, SiteEnv m ~ App)
|
||||||
|
=> AP.Audience URIMode
|
||||||
|
-> ExceptT Text m (Maybe (ParsedAudience URIMode))
|
||||||
|
parseAudience audience = do
|
||||||
|
let recips = concatRecipients audience
|
||||||
|
for (nonEmpty recips) $ \ recipsNE -> do
|
||||||
|
(localsSet, remotes) <- parseRecipients recipsNE
|
||||||
|
let remotesGrouped =
|
||||||
|
groupByHost $ remotes \\ AP.audienceNonActors audience
|
||||||
|
hosts = map fst remotesGrouped
|
||||||
|
return ParsedAudience
|
||||||
|
{ paudLocalRecips = localsSet
|
||||||
|
, paudRemoteActors = remotesGrouped
|
||||||
|
, paudBlinded =
|
||||||
|
audience { AP.audienceBto = [], AP.audienceBcc = [] }
|
||||||
|
, paudFwdHosts =
|
||||||
|
let nonActorHosts =
|
||||||
|
LO.nubSort $
|
||||||
|
map objUriAuthority $ AP.audienceNonActors audience
|
||||||
|
in LO.isect hosts nonActorHosts
|
||||||
|
}
|
||||||
|
where
|
||||||
|
groupByHost :: [FedURI] -> [(Host, NonEmpty LocalURI)]
|
||||||
|
groupByHost = groupAllExtract objUriAuthority objUriLocal
|
||||||
|
|
||||||
|
data Aud u
|
||||||
|
= AudLocal [LocalActor] [LocalStage]
|
||||||
|
| AudRemote (Authority u) [LocalURI] [LocalURI]
|
||||||
|
|
||||||
|
collectAudience
|
||||||
|
:: Foldable f
|
||||||
|
=> f (Aud u)
|
||||||
|
-> ( RecipientRoutes
|
||||||
|
, [(Authority u, NonEmpty LocalURI)]
|
||||||
|
, [Authority u]
|
||||||
|
, [Route App]
|
||||||
|
, [ObjURI u]
|
||||||
|
)
|
||||||
|
collectAudience auds =
|
||||||
|
let (locals, remotes) = partitionAudience auds
|
||||||
|
(actors, stages) =
|
||||||
|
let organize = LO.nubSort . concat
|
||||||
|
in bimap organize organize $ unzip locals
|
||||||
|
groupedRemotes =
|
||||||
|
let organize = LO.nubSort . sconcat
|
||||||
|
in map (second $ bimap organize organize . NE.unzip) $
|
||||||
|
groupAllExtract fst snd remotes
|
||||||
|
in ( makeRecipientSet actors stages
|
||||||
|
, mapMaybe (\ (h, (as, _)) -> (h,) <$> nonEmpty as) groupedRemotes
|
||||||
|
, [ h | (h, (_, cs)) <- groupedRemotes, not (null cs) ]
|
||||||
|
, map renderLocalActor actors ++ map renderLocalStage stages
|
||||||
|
, concatMap (\ (h, (as, cs)) -> ObjURI h <$> as ++ cs) groupedRemotes
|
||||||
|
)
|
||||||
|
where
|
||||||
|
partitionAudience = foldl' f ([], [])
|
||||||
|
where
|
||||||
|
f (ls, rs) (AudLocal as cs) = ((as, cs) : ls, rs)
|
||||||
|
f (ls, rs) (AudRemote h as cs) = (ls , (h, (as, cs)) : rs)
|
|
@ -1,6 +1,6 @@
|
||||||
{- This file is part of Vervis.
|
{- This file is part of Vervis.
|
||||||
-
|
-
|
||||||
- Written in 2016, 2019 by fr33domlover <fr33domlover@riseup.net>.
|
- Written in 2016, 2019, 2022 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.
|
||||||
-
|
-
|
||||||
|
@ -45,10 +45,14 @@ import System.Directory (doesFileExist, doesDirectoryExist)
|
||||||
import System.Environment
|
import System.Environment
|
||||||
import System.FilePath ((</>))
|
import System.FilePath ((</>))
|
||||||
import System.Process (CreateProcess (..), StdStream (..), createProcess, proc)
|
import System.Process (CreateProcess (..), StdStream (..), createProcess, proc)
|
||||||
|
import Web.Hashids
|
||||||
|
import Yesod.Core.Dispatch
|
||||||
|
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import qualified Formatting as F
|
import qualified Formatting as F
|
||||||
|
|
||||||
|
import Yesod.Hashids
|
||||||
|
|
||||||
import Vervis.Access
|
import Vervis.Access
|
||||||
import Vervis.Model
|
import Vervis.Model
|
||||||
import Vervis.Model.Ident
|
import Vervis.Model.Ident
|
||||||
|
@ -69,16 +73,11 @@ type Session = SessionT SessionBase UserAuthId ChannelBase
|
||||||
type SshChanDB = SqlPersistT Channel
|
type SshChanDB = SqlPersistT Channel
|
||||||
type SshSessDB = SqlPersistT Session
|
type SshSessDB = SqlPersistT Session
|
||||||
|
|
||||||
data RepoSpec
|
|
||||||
= SpecUserRepo ShrIdent RpIdent
|
|
||||||
| SpecRepo RpIdent
|
|
||||||
deriving Show
|
|
||||||
|
|
||||||
data Action
|
data Action
|
||||||
= DarcsTransferMode RepoSpec
|
= DarcsTransferMode (KeyHashid Repo)
|
||||||
| DarcsApply RepoSpec
|
| DarcsApply (KeyHashid Repo)
|
||||||
| GitUploadPack RepoSpec
|
| GitUploadPack (KeyHashid Repo)
|
||||||
| GitReceivePack RepoSpec
|
| GitReceivePack (KeyHashid Repo)
|
||||||
deriving Show
|
deriving Show
|
||||||
|
|
||||||
-- | Result of running an action on the server side as a response to an SSH
|
-- | Result of running an action on the server side as a response to an SSH
|
||||||
|
@ -139,24 +138,23 @@ authorize (PublicKey name key) = do
|
||||||
-- Actions
|
-- Actions
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
toKeyHashid t =
|
||||||
|
case fromPathPiece t of
|
||||||
|
Nothing -> fail "Can't parse keyhashid"
|
||||||
|
Just kh -> return kh
|
||||||
|
|
||||||
--TOD TODO TODO check paths for safety... no /./ or /../ and so on
|
--TOD TODO TODO check paths for safety... no /./ or /../ and so on
|
||||||
|
|
||||||
darcsRepoSpecP :: Parser RepoSpec
|
darcsRepoSpecP :: Parser (KeyHashid Repo)
|
||||||
darcsRepoSpecP = f <$>
|
darcsRepoSpecP = toKeyHashid =<< (part <* optional (char '/'))
|
||||||
part <*>
|
|
||||||
optional (char '/' *> optional (part <* optional (char '/')))
|
|
||||||
where
|
where
|
||||||
f sharer (Just (Just repo)) = SpecUserRepo (text2shr sharer) (text2rp repo)
|
|
||||||
f repo _ = SpecRepo (text2rp repo)
|
|
||||||
part = takeWhile1 $ \ c -> c /= '/' && c /= '\''
|
part = takeWhile1 $ \ c -> c /= '/' && c /= '\''
|
||||||
|
|
||||||
gitRepoSpecP :: Parser RepoSpec
|
gitRepoSpecP :: Parser (KeyHashid Repo)
|
||||||
gitRepoSpecP = f <$> (msh *> part) <*> optional (char '/' *> part)
|
gitRepoSpecP = toKeyHashid =<< (msh *> part)
|
||||||
where
|
where
|
||||||
f repo Nothing = SpecRepo (text2rp repo)
|
|
||||||
f sharer (Just repo) = SpecUserRepo (text2shr sharer) (text2rp repo)
|
|
||||||
part = takeWhile1 $ \ c -> c /= '/' && c /= '\''
|
part = takeWhile1 $ \ c -> c /= '/' && c /= '\''
|
||||||
msh = optional (satisfy $ \ c -> c == '/' || c == '~')
|
msh = optional $ satisfy $ \ c -> c == '/' || c == '~'
|
||||||
|
|
||||||
actionP :: Parser Action
|
actionP :: Parser Action
|
||||||
actionP = DarcsTransferMode <$>
|
actionP = DarcsTransferMode <$>
|
||||||
|
@ -178,17 +176,6 @@ detectAction (Execute s) =
|
||||||
Right action -> Right action
|
Right action -> Right action
|
||||||
detectAction _ = Left "Unsupported channel request"
|
detectAction _ = Left "Unsupported channel request"
|
||||||
|
|
||||||
resolveSpec :: RepoSpec -> Channel (ShrIdent, RpIdent)
|
|
||||||
resolveSpec (SpecUserRepo u r) = return (u, r)
|
|
||||||
resolveSpec (SpecRepo r) = do
|
|
||||||
u <- text2shr . T.pack . authUser <$> askAuthDetails
|
|
||||||
return (u, r)
|
|
||||||
|
|
||||||
resolveSpec' :: FilePath -> RepoSpec -> Channel (ShrIdent, RpIdent, FilePath)
|
|
||||||
resolveSpec' root spec = do
|
|
||||||
(u, r) <- resolveSpec spec
|
|
||||||
return (u, r, repoDir root u r)
|
|
||||||
|
|
||||||
execute :: FilePath -> [String] -> Channel ()
|
execute :: FilePath -> [String] -> Channel ()
|
||||||
execute cmd args = do
|
execute cmd args = do
|
||||||
lift $ $logDebugS src $
|
lift $ $logDebugS src $
|
||||||
|
@ -229,26 +216,34 @@ whenGitRepoExists
|
||||||
:: Bool -> FilePath -> Channel ActionResult -> Channel ActionResult
|
:: Bool -> FilePath -> Channel ActionResult -> Channel ActionResult
|
||||||
whenGitRepoExists = whenRepoExists "Git" $ isRepo . fromString
|
whenGitRepoExists = whenRepoExists "Git" $ isRepo . fromString
|
||||||
|
|
||||||
canPushTo :: ShrIdent -> RpIdent -> Channel Bool
|
canPushTo :: RepoId -> Channel Bool
|
||||||
canPushTo shr rp = do
|
canPushTo repoID = do
|
||||||
pid <- authId <$> askAuthDetails
|
pid <- authId <$> askAuthDetails
|
||||||
oas <- runChanDB $ checkRepoAccess (Just pid) ProjOpPush shr rp
|
oas <- runChanDB $ checkRepoAccess' (Just pid) ProjOpPush repoID
|
||||||
return $
|
return $
|
||||||
case oas of
|
case oas of
|
||||||
ObjectAccessAllowed -> True
|
ObjectAccessAllowed -> True
|
||||||
_ -> False
|
_ -> False
|
||||||
|
|
||||||
runAction :: FilePath -> Bool -> Action -> Channel ActionResult
|
runAction
|
||||||
runAction repoDir _wantReply action =
|
:: (KeyHashid Repo -> Maybe RepoId)
|
||||||
|
-> FilePath
|
||||||
|
-> Bool
|
||||||
|
-> Action
|
||||||
|
-> Channel ActionResult
|
||||||
|
runAction decodeRepoHash root _wantReply action =
|
||||||
case action of
|
case action of
|
||||||
DarcsTransferMode spec -> do
|
DarcsTransferMode repoHash -> do
|
||||||
(_sharer, _repo, repoPath) <- resolveSpec' repoDir spec
|
let repoPath = repoDir root repoHash
|
||||||
whenDarcsRepoExists False repoPath $ do
|
whenDarcsRepoExists False repoPath $ do
|
||||||
execute "darcs" ["transfer-mode", "--repodir", repoPath]
|
execute "darcs" ["transfer-mode", "--repodir", repoPath]
|
||||||
return ARProcess
|
return ARProcess
|
||||||
DarcsApply spec -> do
|
DarcsApply repoHash -> do
|
||||||
(sharer, repo, repoPath) <- resolveSpec' repoDir spec
|
let repoPath = repoDir root repoHash
|
||||||
can <- canPushTo sharer repo
|
can <-
|
||||||
|
case decodeRepoHash repoHash of
|
||||||
|
Nothing -> return False
|
||||||
|
Just repoID -> canPushTo repoID
|
||||||
if can
|
if can
|
||||||
then whenDarcsRepoExists True repoPath $ do
|
then whenDarcsRepoExists True repoPath $ do
|
||||||
pid <- authId <$> askAuthDetails
|
pid <- authId <$> askAuthDetails
|
||||||
|
@ -256,14 +251,17 @@ runAction repoDir _wantReply action =
|
||||||
execute "darcs" ["apply", "--all", "--repodir", repoPath]
|
execute "darcs" ["apply", "--all", "--repodir", repoPath]
|
||||||
return ARProcess
|
return ARProcess
|
||||||
else return $ ARFail "You can't push to this repository"
|
else return $ ARFail "You can't push to this repository"
|
||||||
GitUploadPack spec -> do
|
GitUploadPack repoHash -> do
|
||||||
(_sharer, _repo, repoPath) <- resolveSpec' repoDir spec
|
let repoPath = repoDir root repoHash
|
||||||
whenGitRepoExists False repoPath $ do
|
whenGitRepoExists False repoPath $ do
|
||||||
execute "git-upload-pack" [repoPath]
|
execute "git-upload-pack" [repoPath]
|
||||||
return ARProcess
|
return ARProcess
|
||||||
GitReceivePack spec -> do
|
GitReceivePack repoHash -> do
|
||||||
(sharer, repo, repoPath) <- resolveSpec' repoDir spec
|
let repoPath = repoDir root repoHash
|
||||||
can <- canPushTo sharer repo
|
can <-
|
||||||
|
case decodeRepoHash repoHash of
|
||||||
|
Nothing -> return False
|
||||||
|
Just repoID -> canPushTo repoID
|
||||||
if can
|
if can
|
||||||
then whenGitRepoExists True repoPath $ do
|
then whenGitRepoExists True repoPath $ do
|
||||||
pid <- authId <$> askAuthDetails
|
pid <- authId <$> askAuthDetails
|
||||||
|
@ -272,8 +270,13 @@ runAction repoDir _wantReply action =
|
||||||
return ARProcess
|
return ARProcess
|
||||||
else return $ ARFail "You can't push to this repository"
|
else return $ ARFail "You can't push to this repository"
|
||||||
|
|
||||||
handle :: FilePath -> Bool -> ChannelRequest -> Channel ()
|
handle
|
||||||
handle repoDir wantReply request = do
|
:: (KeyHashid Repo -> Maybe RepoId)
|
||||||
|
-> FilePath
|
||||||
|
-> Bool
|
||||||
|
-> ChannelRequest
|
||||||
|
-> Channel ()
|
||||||
|
handle decodeRepoHash repoDir wantReply request = do
|
||||||
lift $ $logDebugS src $ T.pack $ show request
|
lift $ $logDebugS src $ T.pack $ show request
|
||||||
case detectAction request of
|
case detectAction request of
|
||||||
Left e -> do
|
Left e -> do
|
||||||
|
@ -282,7 +285,7 @@ handle repoDir wantReply request = do
|
||||||
when wantReply channelFail
|
when wantReply channelFail
|
||||||
Right act -> do
|
Right act -> do
|
||||||
lift $ $logDebugS src $ T.pack $ show act
|
lift $ $logDebugS src $ T.pack $ show act
|
||||||
res <- runAction repoDir wantReply act
|
res <- runAction decodeRepoHash repoDir wantReply act
|
||||||
case res of
|
case res of
|
||||||
ARDone msg -> do
|
ARDone msg -> do
|
||||||
lift $ $logDebugS src $ "Action done: " <> msg
|
lift $ $logDebugS src $ "Action done: " <> msg
|
||||||
|
@ -307,10 +310,11 @@ ready = runLoggingT $ $logInfoS src "SSH server component starting"
|
||||||
|
|
||||||
mkConfig
|
mkConfig
|
||||||
:: AppSettings
|
:: AppSettings
|
||||||
|
-> HashidsContext
|
||||||
-> ConnectionPool
|
-> ConnectionPool
|
||||||
-> LogFunc
|
-> LogFunc
|
||||||
-> IO (Config SessionBase ChannelBase UserAuthId)
|
-> IO (Config SessionBase ChannelBase UserAuthId)
|
||||||
mkConfig settings pool logFunc = do
|
mkConfig settings ctx pool logFunc = do
|
||||||
keyPair <- keyPairFromFile $ appSshKeyFile settings
|
keyPair <- keyPairFromFile $ appSshKeyFile settings
|
||||||
return $ Config
|
return $ Config
|
||||||
{ cSession = SessionConfig
|
{ cSession = SessionConfig
|
||||||
|
@ -321,7 +325,7 @@ mkConfig settings pool logFunc = do
|
||||||
flip runReaderT pool . flip runLoggingT logFunc
|
flip runReaderT pool . flip runLoggingT logFunc
|
||||||
}
|
}
|
||||||
, cChannel = ChannelConfig
|
, cChannel = ChannelConfig
|
||||||
{ ccRequestHandler = handle $ appRepoDir settings
|
{ ccRequestHandler = handle (decodeKeyHashidPure ctx) (appRepoDir settings)
|
||||||
, ccRunBaseMonad =
|
, ccRunBaseMonad =
|
||||||
flip runReaderT pool . flip runLoggingT logFunc
|
flip runReaderT pool . flip runLoggingT logFunc
|
||||||
}
|
}
|
||||||
|
@ -329,7 +333,7 @@ mkConfig settings pool logFunc = do
|
||||||
, cReadyAction = ready logFunc
|
, cReadyAction = ready logFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
runSsh :: AppSettings -> ConnectionPool -> LogFunc -> IO ()
|
runSsh :: AppSettings -> HashidsContext -> ConnectionPool -> LogFunc -> IO ()
|
||||||
runSsh settings pool logFunc = do
|
runSsh settings ctx pool logFunc = do
|
||||||
config <- mkConfig settings pool logFunc
|
config <- mkConfig settings ctx pool logFunc
|
||||||
startConfig config
|
startConfig config
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{- This file is part of Vervis.
|
{- This file is part of Vervis.
|
||||||
-
|
-
|
||||||
- Written in 2016, 2018, 2019, 2020, 2021
|
- Written in 2016, 2018, 2019, 2020, 2021, 2022
|
||||||
- by fr33domlover <fr33domlover@riseup.net>.
|
- 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.
|
||||||
|
@ -15,7 +15,9 @@
|
||||||
-}
|
-}
|
||||||
|
|
||||||
module Vervis.Ticket
|
module Vervis.Ticket
|
||||||
( getTicketSummaries
|
(
|
||||||
|
{-
|
||||||
|
getTicketSummaries
|
||||||
--, getTicketDepEdges
|
--, getTicketDepEdges
|
||||||
, WorkflowFieldFilter (..)
|
, WorkflowFieldFilter (..)
|
||||||
, WorkflowFieldSummary (..)
|
, WorkflowFieldSummary (..)
|
||||||
|
@ -28,14 +30,13 @@ module Vervis.Ticket
|
||||||
, getTicketEnumParams
|
, getTicketEnumParams
|
||||||
, TicketClassParam (..)
|
, TicketClassParam (..)
|
||||||
, getTicketClasses
|
, getTicketClasses
|
||||||
, getSharerTicket
|
-}
|
||||||
, getSharerTicket404
|
|
||||||
, getProjectTicket
|
|
||||||
, getProjectTicket404
|
|
||||||
|
|
||||||
, getSharerWorkItems
|
getTicket
|
||||||
, getDependencyCollection
|
, getTicket404
|
||||||
, getReverseDependencyCollection
|
|
||||||
|
--, getDependencyCollection
|
||||||
|
--, getReverseDependencyCollection
|
||||||
|
|
||||||
, WorkItem (..)
|
, WorkItem (..)
|
||||||
, getWorkItemRoute
|
, getWorkItemRoute
|
||||||
|
@ -43,7 +44,6 @@ module Vervis.Ticket
|
||||||
, getWorkItem
|
, getWorkItem
|
||||||
, parseWorkItem
|
, parseWorkItem
|
||||||
, parseProposalBundle
|
, parseProposalBundle
|
||||||
, getRemoteTicketByURI
|
|
||||||
|
|
||||||
, checkDepAndTarget
|
, checkDepAndTarget
|
||||||
)
|
)
|
||||||
|
@ -81,15 +81,15 @@ import Data.Paginate.Local
|
||||||
import Database.Persist.Local
|
import Database.Persist.Local
|
||||||
import Yesod.Persist.Local
|
import Yesod.Persist.Local
|
||||||
|
|
||||||
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.Model.Workflow
|
import Vervis.Model.Workflow
|
||||||
import Vervis.Paginate
|
import Vervis.Paginate
|
||||||
import Vervis.Widget.Ticket (TicketSummary (..))
|
import Vervis.Recipient
|
||||||
|
|
||||||
|
{-
|
||||||
-- | Get summaries of all the tickets in the given project.
|
-- | Get summaries of all the tickets in the given project.
|
||||||
getTicketSummaries
|
getTicketSummaries
|
||||||
:: Maybe (E.SqlExpr (Entity Ticket) -> E.SqlExpr (E.Value Bool))
|
:: Maybe (E.SqlExpr (Entity Ticket) -> E.SqlExpr (E.Value Bool))
|
||||||
|
@ -464,23 +464,18 @@ getTicketClasses tid wid = fmap (map toCParam) $
|
||||||
, f E.^. WorkflowFieldFilterClosed
|
, f E.^. WorkflowFieldFilterClosed
|
||||||
, p E.?. TicketParamClassId
|
, p E.?. TicketParamClassId
|
||||||
)
|
)
|
||||||
|
-}
|
||||||
|
|
||||||
getSharerTicket
|
getTicket
|
||||||
:: MonadIO m
|
:: MonadIO m
|
||||||
=> ShrIdent
|
=> DeckId
|
||||||
-> TicketAuthorLocalId
|
-> TicketDeckId
|
||||||
-> ReaderT SqlBackend m
|
-> ReaderT SqlBackend m
|
||||||
( Maybe
|
( Maybe
|
||||||
( Entity TicketAuthorLocal
|
( Entity Deck
|
||||||
, Entity LocalTicket
|
, Entity TicketDeck
|
||||||
, Entity Ticket
|
, Entity Ticket
|
||||||
, Either
|
, Either (Entity TicketAuthorLocal) (Entity TicketAuthorRemote)
|
||||||
( Entity TicketContextLocal
|
|
||||||
, Entity TicketProjectLocal
|
|
||||||
)
|
|
||||||
( Entity TicketProjectRemote
|
|
||||||
, Maybe (Entity TicketProjectRemoteAccept)
|
|
||||||
)
|
|
||||||
, Maybe
|
, Maybe
|
||||||
( Entity TicketResolve
|
( Entity TicketResolve
|
||||||
, Either
|
, Either
|
||||||
|
@ -489,151 +484,57 @@ getSharerTicket
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
getSharerTicket shr talid = runMaybeT $ do
|
getTicket did tdid = runMaybeT $ do
|
||||||
pid <- do
|
d <- MaybeT $ get did
|
||||||
sid <- MaybeT $ getKeyBy $ UniqueSharer shr
|
td <- MaybeT $ get tdid
|
||||||
MaybeT $ getKeyBy $ UniquePersonIdent sid
|
guard $ ticketDeckDeck td == did
|
||||||
tal <- MaybeT $ get talid
|
|
||||||
guard $ ticketAuthorLocalAuthor tal == pid
|
let tid = ticketDeckTicket td
|
||||||
let ltid = ticketAuthorLocalTicket tal
|
|
||||||
lt <- lift $ getJust ltid
|
|
||||||
let tid = localTicketTicket lt
|
|
||||||
t <- lift $ getJust tid
|
t <- lift $ getJust tid
|
||||||
mbn <- lift $ selectFirst [BundleTicket ==. tid] []
|
|
||||||
guard $ isNothing mbn
|
|
||||||
project <-
|
|
||||||
requireEitherAlt
|
|
||||||
(do mtcl <- lift $ getBy $ UniqueTicketContextLocal tid
|
|
||||||
for mtcl $ \ etcl@(Entity tclid _) -> do
|
|
||||||
etpl <- MaybeT $ getBy $ UniqueTicketProjectLocal 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, etpl)
|
|
||||||
)
|
|
||||||
(do mtpr <- lift $ getBy $ UniqueTicketProjectRemote talid
|
|
||||||
lift $ for mtpr $ \ etpr@(Entity tprid _) ->
|
|
||||||
(etpr,) <$> getBy (UniqueTicketProjectRemoteAccept tprid)
|
|
||||||
)
|
|
||||||
"Ticket doesn't have project"
|
|
||||||
"Ticket has both local and remote project"
|
|
||||||
mresolved <- lift $ getResolved ltid
|
|
||||||
return (Entity talid tal, Entity ltid lt, Entity tid t, project, mresolved)
|
|
||||||
|
|
||||||
getSharerTicket404
|
author <-
|
||||||
:: ShrIdent
|
lift $
|
||||||
-> KeyHashid TicketAuthorLocal
|
|
||||||
-> AppDB
|
|
||||||
( Entity TicketAuthorLocal
|
|
||||||
, Entity LocalTicket
|
|
||||||
, Entity Ticket
|
|
||||||
, Either
|
|
||||||
( Entity TicketContextLocal
|
|
||||||
, Entity TicketProjectLocal
|
|
||||||
)
|
|
||||||
( Entity TicketProjectRemote
|
|
||||||
, Maybe (Entity TicketProjectRemoteAccept)
|
|
||||||
)
|
|
||||||
, Maybe
|
|
||||||
( Entity TicketResolve
|
|
||||||
, Either
|
|
||||||
(Entity TicketResolveLocal)
|
|
||||||
(Entity TicketResolveRemote)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
getSharerTicket404 shr talkhid = do
|
|
||||||
talid <- decodeKeyHashid404 talkhid
|
|
||||||
mticket <- getSharerTicket shr talid
|
|
||||||
case mticket of
|
|
||||||
Nothing -> notFound
|
|
||||||
Just ticket -> return ticket
|
|
||||||
|
|
||||||
getResolved
|
|
||||||
:: MonadIO m
|
|
||||||
=> LocalTicketId
|
|
||||||
-> ReaderT SqlBackend m
|
|
||||||
(Maybe
|
|
||||||
( Entity TicketResolve
|
|
||||||
, Either (Entity TicketResolveLocal) (Entity TicketResolveRemote)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
getResolved ltid = do
|
|
||||||
metr <- getBy $ UniqueTicketResolve ltid
|
|
||||||
for metr $ \ etr@(Entity trid _) ->
|
|
||||||
(etr,) <$>
|
|
||||||
requireEitherAlt
|
requireEitherAlt
|
||||||
(getBy $ UniqueTicketResolveLocal trid)
|
(getBy $ UniqueTicketAuthorLocal tid)
|
||||||
(getBy $ UniqueTicketResolveRemote trid)
|
(getBy $ UniqueTicketAuthorRemote tid)
|
||||||
"No TRX"
|
"Ticket doesn't have author"
|
||||||
"Both TRL and TRR"
|
"Ticket has both local and remote author"
|
||||||
|
|
||||||
getProjectTicket
|
mresolved <- lift $ getResolved tid
|
||||||
:: MonadIO m
|
|
||||||
=> ShrIdent
|
return (Entity did d, Entity tdid td, Entity tid t, author, mresolved)
|
||||||
-> PrjIdent
|
|
||||||
-> LocalTicketId
|
where
|
||||||
-> ReaderT SqlBackend m
|
|
||||||
( Maybe
|
getResolved
|
||||||
( Entity Sharer
|
:: MonadIO m
|
||||||
, Entity Project
|
=> TicketId
|
||||||
, Entity Ticket
|
-> ReaderT SqlBackend m
|
||||||
, Entity LocalTicket
|
(Maybe
|
||||||
, Entity TicketContextLocal
|
|
||||||
, Entity TicketProjectLocal
|
|
||||||
, Either
|
|
||||||
(Entity TicketAuthorLocal, Entity TicketUnderProject)
|
|
||||||
(Entity TicketAuthorRemote)
|
|
||||||
, Maybe
|
|
||||||
( Entity TicketResolve
|
( Entity TicketResolve
|
||||||
, Either
|
, Either
|
||||||
(Entity TicketResolveLocal)
|
(Entity TicketResolveLocal)
|
||||||
(Entity TicketResolveRemote)
|
(Entity TicketResolveRemote)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
getResolved tid = do
|
||||||
getProjectTicket shr prj ltid = runMaybeT $ do
|
metr <- getBy $ UniqueTicketResolve tid
|
||||||
es@(Entity sid _) <- MaybeT $ getBy $ UniqueSharer shr
|
for metr $ \ etr@(Entity trid _) ->
|
||||||
ej@(Entity jid _) <- MaybeT $ getBy $ UniqueProject prj sid
|
(etr,) <$>
|
||||||
lt <- MaybeT $ get ltid
|
requireEitherAlt
|
||||||
let tid = localTicketTicket lt
|
(getBy $ UniqueTicketResolveLocal trid)
|
||||||
t <- MaybeT $ get tid
|
(getBy $ UniqueTicketResolveRemote trid)
|
||||||
etcl@(Entity tclid _) <- MaybeT $ getBy $ UniqueTicketContextLocal tid
|
"No TRX"
|
||||||
etpl@(Entity _ tpl) <- MaybeT $ getBy $ UniqueTicketProjectLocal tclid
|
"Both TRL and TRR"
|
||||||
guard $ ticketProjectLocalProject tpl == jid
|
|
||||||
mbn <- lift $ selectFirst [BundleTicket ==. tid] []
|
|
||||||
guard $ isNothing mbn
|
|
||||||
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)
|
|
||||||
"Ticket doesn't have author"
|
|
||||||
"Ticket has both local and remote author"
|
|
||||||
mresolved <- lift $ getResolved ltid
|
|
||||||
return (es, ej, Entity tid t, Entity ltid lt, etcl, etpl, author, mresolved)
|
|
||||||
|
|
||||||
getProjectTicket404
|
getTicket404
|
||||||
:: ShrIdent
|
:: KeyHashid Deck
|
||||||
-> PrjIdent
|
-> KeyHashid TicketDeck
|
||||||
-> KeyHashid LocalTicket
|
|
||||||
-> AppDB
|
-> AppDB
|
||||||
( Entity Sharer
|
( Entity Deck
|
||||||
, Entity Project
|
, Entity TicketDeck
|
||||||
, Entity Ticket
|
, Entity Ticket
|
||||||
, Entity LocalTicket
|
, Either (Entity TicketAuthorLocal) (Entity TicketAuthorRemote)
|
||||||
, Entity TicketContextLocal
|
|
||||||
, Entity TicketProjectLocal
|
|
||||||
, Either
|
|
||||||
(Entity TicketAuthorLocal, Entity TicketUnderProject)
|
|
||||||
(Entity TicketAuthorRemote)
|
|
||||||
, Maybe
|
, Maybe
|
||||||
( Entity TicketResolve
|
( Entity TicketResolve
|
||||||
, Either
|
, Either
|
||||||
|
@ -641,73 +542,21 @@ getProjectTicket404
|
||||||
(Entity TicketResolveRemote)
|
(Entity TicketResolveRemote)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
getProjectTicket404 shr prj ltkhid = do
|
getTicket404 dkhid tdkhid = do
|
||||||
ltid <- decodeKeyHashid404 ltkhid
|
did <- decodeKeyHashid404 dkhid
|
||||||
mticket <- getProjectTicket shr prj ltid
|
tdid <- decodeKeyHashid404 tdkhid
|
||||||
|
mticket <- getTicket did tdid
|
||||||
case mticket of
|
case mticket of
|
||||||
Nothing -> notFound
|
Nothing -> notFound
|
||||||
Just ticket -> return ticket
|
Just ticket -> return ticket
|
||||||
|
|
||||||
getSharerWorkItems
|
{-
|
||||||
:: ToBackendKey SqlBackend record
|
|
||||||
=> (ShrIdent -> Route App)
|
|
||||||
-> (ShrIdent -> KeyHashid record -> Route App)
|
|
||||||
-> (PersonId -> AppDB Int)
|
|
||||||
-> (PersonId -> Int -> Int -> AppDB [E.Value (Key record)])
|
|
||||||
-> ShrIdent
|
|
||||||
-> Handler TypedContent
|
|
||||||
getSharerWorkItems mkhere itemRoute countItems selectItems shr = do
|
|
||||||
(total, pages, mpage) <- runDB $ do
|
|
||||||
sid <- getKeyBy404 $ UniqueSharer shr
|
|
||||||
pid <- getKeyBy404 $ UniquePersonIdent sid
|
|
||||||
getPageAndNavCount (countItems pid) (selectItems pid)
|
|
||||||
encodeRouteHome <- getEncodeRouteHome
|
|
||||||
encodeRouteLocal <- getEncodeRouteLocal
|
|
||||||
encodeRoutePageLocal <- getEncodeRoutePageLocal
|
|
||||||
let here = mkhere shr
|
|
||||||
pageUrl = encodeRoutePageLocal here
|
|
||||||
encodeTicketKey <- getEncodeKeyHashid
|
|
||||||
let ticketUrl = itemRoute shr . encodeTicketKey
|
|
||||||
|
|
||||||
case mpage of
|
|
||||||
Nothing -> provide here $ Collection
|
|
||||||
{ collectionId = encodeRouteLocal here
|
|
||||||
, collectionType = CollectionTypeOrdered
|
|
||||||
, collectionTotalItems = Just total
|
|
||||||
, collectionCurrent = Nothing
|
|
||||||
, collectionFirst = Just $ pageUrl 1
|
|
||||||
, collectionLast = Just $ pageUrl pages
|
|
||||||
, collectionItems = [] :: [Text]
|
|
||||||
}
|
|
||||||
Just (tickets, navModel) ->
|
|
||||||
let current = nmCurrent navModel
|
|
||||||
in provide here $ 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 . ticketUrl . E.unValue) tickets
|
|
||||||
}
|
|
||||||
where
|
|
||||||
provide :: ActivityPub a => Route App -> a URIMode -> Handler TypedContent
|
|
||||||
provide here a = provideHtmlAndAP a $ redirectToPrettyJSON here
|
|
||||||
|
|
||||||
getDependencyCollection
|
getDependencyCollection
|
||||||
:: Route App -> AppDB LocalTicketId -> Handler TypedContent
|
:: Route App
|
||||||
getDependencyCollection here getLocalTicketId404 = do
|
-> (KeyHashid LocalTicket -> Route App)
|
||||||
|
-> AppDB LocalTicketId
|
||||||
|
-> Handler TypedContent
|
||||||
|
getDependencyCollection here depRoute getLocalTicketId404 = do
|
||||||
tdids <- runDB $ do
|
tdids <- runDB $ do
|
||||||
ltid <- getLocalTicketId404
|
ltid <- getLocalTicketId404
|
||||||
selectKeysList
|
selectKeysList
|
||||||
|
@ -724,7 +573,7 @@ getDependencyCollection here getLocalTicketId404 = do
|
||||||
, collectionFirst = Nothing
|
, collectionFirst = Nothing
|
||||||
, collectionLast = Nothing
|
, collectionLast = Nothing
|
||||||
, collectionItems =
|
, collectionItems =
|
||||||
map (encodeRouteHome . TicketDepR . encodeHid) tdids
|
map (encodeRouteHome . depRoute . encodeHid) tdids
|
||||||
}
|
}
|
||||||
provideHtmlAndAP deps $ redirectToPrettyJSON here
|
provideHtmlAndAP deps $ redirectToPrettyJSON here
|
||||||
|
|
||||||
|
@ -759,11 +608,11 @@ getReverseDependencyCollection here getLocalTicketId404 = do
|
||||||
E.on $ rtd E.^. RemoteTicketDependencyIdent E.==. ro E.^. RemoteObjectId
|
E.on $ rtd E.^. RemoteTicketDependencyIdent E.==. ro E.^. RemoteObjectId
|
||||||
E.where_ $ rtd E.^. RemoteTicketDependencyChild E.==. E.val ltid
|
E.where_ $ rtd E.^. RemoteTicketDependencyChild E.==. E.val ltid
|
||||||
return (i E.^. InstanceHost, ro E.^. RemoteObjectIdent)
|
return (i E.^. InstanceHost, ro E.^. RemoteObjectIdent)
|
||||||
|
-}
|
||||||
|
|
||||||
data WorkItem
|
data WorkItem
|
||||||
= WorkItemSharerTicket ShrIdent TicketAuthorLocalId Bool
|
= WorkItemTicket DeckId TicketDeckId
|
||||||
| WorkItemProjectTicket ShrIdent PrjIdent LocalTicketId
|
| WorkItemCloth LoomId TicketLoomId
|
||||||
| WorkItemRepoProposal ShrIdent RpIdent LocalTicketId
|
|
||||||
deriving Eq
|
deriving Eq
|
||||||
|
|
||||||
getWorkItemRoute
|
getWorkItemRoute
|
||||||
|
@ -773,99 +622,26 @@ getWorkItemRoute wi = ($ wi) <$> askWorkItemRoute
|
||||||
askWorkItemRoute
|
askWorkItemRoute
|
||||||
:: (MonadSite m, YesodHashids (SiteEnv m)) => m (WorkItem -> Route App)
|
:: (MonadSite m, YesodHashids (SiteEnv m)) => m (WorkItem -> Route App)
|
||||||
askWorkItemRoute = do
|
askWorkItemRoute = do
|
||||||
hashTALID <- getEncodeKeyHashid
|
hashDID <- getEncodeKeyHashid
|
||||||
hashLTID <- getEncodeKeyHashid
|
hashLID <- getEncodeKeyHashid
|
||||||
let route (WorkItemSharerTicket shr talid False) = SharerTicketR shr (hashTALID talid)
|
hashTDID <- getEncodeKeyHashid
|
||||||
route (WorkItemSharerTicket shr talid True) = SharerProposalR shr (hashTALID talid)
|
hashTLID <- getEncodeKeyHashid
|
||||||
route (WorkItemProjectTicket shr prj ltid) = ProjectTicketR shr prj (hashLTID ltid)
|
let route (WorkItemTicket did tdid) = TicketR (hashDID did) (hashTDID tdid)
|
||||||
route (WorkItemRepoProposal shr rp ltid) = RepoProposalR shr rp (hashLTID ltid)
|
route (WorkItemCloth lid tlid) = ClothR (hashLID lid) (hashTLID tlid)
|
||||||
return route
|
return route
|
||||||
|
|
||||||
getWorkItem :: MonadIO m => LocalTicketId -> ReaderT SqlBackend m WorkItem
|
getWorkItem :: MonadIO m => TicketId -> ReaderT SqlBackend m WorkItem
|
||||||
getWorkItem ltid = (either error return =<<) $ runExceptT $ do
|
getWorkItem tid = do
|
||||||
lt <- lift $ getJust ltid
|
tracker <-
|
||||||
let tid = localTicketTicket lt
|
requireEitherAlt
|
||||||
|
(getBy $ UniqueTicketDeck tid)
|
||||||
metal <- lift $ getBy $ UniqueTicketAuthorLocal ltid
|
(getBy $ UniqueTicketLoom tid)
|
||||||
mremoteContext <-
|
"Neither TD nor TD found"
|
||||||
case metal of
|
"Both TD and TL found"
|
||||||
Nothing -> return Nothing
|
return $
|
||||||
Just (Entity talid _) -> lift $ do
|
case tracker of
|
||||||
metcr <- getBy (UniqueTicketProjectRemote talid)
|
Left (Entity tdid td) -> WorkItemTicket (ticketDeckDeck td) tdid
|
||||||
for metcr $ \ etcr ->
|
Right (Entity tlid tl) -> WorkItemCloth (ticketLoomLoom tl) tlid
|
||||||
(etcr,) . (> 0) <$> count [BundleTicket ==. tid]
|
|
||||||
mlocalContext <- do
|
|
||||||
metcl <- lift $ getBy $ UniqueTicketContextLocal tid
|
|
||||||
for metcl $ \ etcl@(Entity tclid _) -> do
|
|
||||||
mbn <- lift $ selectFirst [BundleTicket ==. tid] []
|
|
||||||
metpl <- lift $ getBy $ UniqueTicketProjectLocal tclid
|
|
||||||
metrl <- lift $ getBy $ UniqueTicketRepoLocal tclid
|
|
||||||
case (metpl, metrl) of
|
|
||||||
(Nothing, Nothing) -> throwE "TCL but no TPL and no TRL"
|
|
||||||
(Just etpl, Nothing) -> do
|
|
||||||
when (isJust mbn) $ throwE "TPL but patches attached"
|
|
||||||
return (etcl, Left etpl)
|
|
||||||
(Nothing, Just etrl) -> do
|
|
||||||
when (isNothing mbn) $ throwE "TRL but no patches attached"
|
|
||||||
return (etcl, Right etrl)
|
|
||||||
(Just _, Just _) -> throwE "Both TPL and TRL"
|
|
||||||
metar <-
|
|
||||||
case mlocalContext of
|
|
||||||
Nothing -> return Nothing
|
|
||||||
Just (Entity tclid _, _) ->
|
|
||||||
lift $ getBy $ UniqueTicketAuthorRemote tclid
|
|
||||||
|
|
||||||
mert <-
|
|
||||||
case metar of
|
|
||||||
Nothing -> return Nothing
|
|
||||||
Just (Entity tarid _) -> lift $ getBy $ UniqueRemoteTicket tarid
|
|
||||||
|
|
||||||
metuc <-
|
|
||||||
case (metal, mlocalContext) of
|
|
||||||
(Nothing, Nothing) -> return Nothing
|
|
||||||
(Just (Entity talid _), Nothing) -> do
|
|
||||||
mtuc <- lift $ getBy $ UniqueTicketUnderProjectAuthor talid
|
|
||||||
for mtuc $ \ _ -> throwE "No TCL, but TUC exists for TAL"
|
|
||||||
(Nothing, Just (Entity tclid _, _)) -> do
|
|
||||||
mtuc <- lift $ getBy $ UniqueTicketUnderProjectProject tclid
|
|
||||||
for mtuc $ \ _ -> throwE "No TAL, but TUC exists for TCL"
|
|
||||||
(Just (Entity talid _), Just (Entity tclid _, _)) -> do
|
|
||||||
metuc1 <- lift $ getBy $ UniqueTicketUnderProjectAuthor talid
|
|
||||||
mtucid2 <- lift $ getKeyBy $ UniqueTicketUnderProjectProject tclid
|
|
||||||
case (metuc1, mtucid2) of
|
|
||||||
(Nothing, Nothing) -> return Nothing
|
|
||||||
(Just _, Nothing) -> throwE "TAL has TUC, TCL doesn't"
|
|
||||||
(Nothing, Just _) -> throwE "TCL has TUC, TAL doesn't"
|
|
||||||
(Just etuc, Just tucid) ->
|
|
||||||
if entityKey etuc == tucid
|
|
||||||
then return $ Just etuc
|
|
||||||
else throwE "TAL and TCL have different TUCs"
|
|
||||||
|
|
||||||
verifyNothingE mert "Ticket has both LT and RT"
|
|
||||||
|
|
||||||
case (mremoteContext, metal, mlocalContext, metar) of
|
|
||||||
(Nothing, Just etal, Just (_, ctx), Nothing) ->
|
|
||||||
lift $
|
|
||||||
case metuc of
|
|
||||||
Nothing -> authorHosted etal (isRight ctx)
|
|
||||||
Just _ -> contextHosted ctx
|
|
||||||
(Nothing, Nothing, Just (_, ctx), Just _) -> lift $ contextHosted ctx
|
|
||||||
(Just (_, patch), Just etal, Nothing, Nothing) ->
|
|
||||||
lift $ authorHosted etal patch
|
|
||||||
_ -> throwE "Invalid/unexpected context/author situation"
|
|
||||||
where
|
|
||||||
contextHosted (Left (Entity _ tpl)) = do
|
|
||||||
j <- getJust $ ticketProjectLocalProject tpl
|
|
||||||
s <- getJust $ projectSharer j
|
|
||||||
return $ WorkItemProjectTicket (sharerIdent s) (projectIdent j) ltid
|
|
||||||
contextHosted (Right (Entity _ trl)) = do
|
|
||||||
r <- getJust $ ticketRepoLocalRepo trl
|
|
||||||
s <- getJust $ repoSharer r
|
|
||||||
return $ WorkItemRepoProposal (sharerIdent s) (repoIdent r) ltid
|
|
||||||
authorHosted (Entity talid tal) patch = do
|
|
||||||
p <- getJust $ ticketAuthorLocalAuthor tal
|
|
||||||
s <- getJust $ personIdent p
|
|
||||||
return $ WorkItemSharerTicket (sharerIdent s) talid patch
|
|
||||||
|
|
||||||
parseWorkItem name u@(ObjURI h lu) = do
|
parseWorkItem name u@(ObjURI h lu) = do
|
||||||
hl <- hostIsLocal h
|
hl <- hostIsLocal h
|
||||||
|
@ -875,18 +651,14 @@ parseWorkItem name u@(ObjURI h lu) = do
|
||||||
fromMaybeE (decodeRouteLocal lu) $
|
fromMaybeE (decodeRouteLocal lu) $
|
||||||
name <> ": Not a valid route"
|
name <> ": Not a valid route"
|
||||||
case route of
|
case route of
|
||||||
SharerTicketR shr talkhid -> do
|
TicketR deck ticket ->
|
||||||
talid <- decodeKeyHashidE talkhid $ name <> ": Invalid talkhid"
|
WorkItemTicket
|
||||||
return $ WorkItemSharerTicket shr talid False
|
<$> decodeKeyHashidE deck (name <> ": Invalid dkhid")
|
||||||
SharerProposalR shr talkhid -> do
|
<*> decodeKeyHashidE ticket (name <> ": Invalid tdkhid")
|
||||||
talid <- decodeKeyHashidE talkhid $ name <> ": Invalid talkhid"
|
ClothR loom ticket ->
|
||||||
return $ WorkItemSharerTicket shr talid True
|
WorkItemCloth
|
||||||
ProjectTicketR shr prj ltkhid -> do
|
<$> decodeKeyHashidE loom (name <> ": Invalid lkhid")
|
||||||
ltid <- decodeKeyHashidE ltkhid $ name <> ": Invalid ltkhid"
|
<*> decodeKeyHashidE ticket (name <> ": Invalid tlkhid")
|
||||||
return $ WorkItemProjectTicket shr prj ltid
|
|
||||||
RepoProposalR shr rp ltkhid -> do
|
|
||||||
ltid <- decodeKeyHashidE ltkhid $ name <> ": Invalid ltkhid"
|
|
||||||
return $ WorkItemRepoProposal shr rp ltid
|
|
||||||
_ -> throwE $ name <> ": not a work item route"
|
_ -> throwE $ name <> ": not a work item route"
|
||||||
else return $ Right u
|
else return $ Right u
|
||||||
|
|
||||||
|
@ -898,63 +670,14 @@ parseProposalBundle name u@(ObjURI h lu) = do
|
||||||
fromMaybeE (decodeRouteLocal lu) $
|
fromMaybeE (decodeRouteLocal lu) $
|
||||||
name <> ": Not a valid route"
|
name <> ": Not a valid route"
|
||||||
case route of
|
case route of
|
||||||
SharerProposalBundleR shr talkhid bnkhid-> do
|
BundleR loom ticket bundle ->
|
||||||
talid <- decodeKeyHashidE talkhid $ name <> ": Invalid talkhid"
|
(,,)
|
||||||
bnid <- decodeKeyHashidE bnkhid $ name <> ": Invalid bnkhid"
|
<$> decodeKeyHashidE loom (name <> ": Invalid lkhid")
|
||||||
return $ Left (shr, talid, bnid)
|
<*> decodeKeyHashidE ticket (name <> ": Invalid tlkhid")
|
||||||
RepoProposalBundleR shr rp ltkhid bnkhid -> do
|
<*> decodeKeyHashidE bundle (name <> ": Invalid bnkhid")
|
||||||
ltid <- decodeKeyHashidE ltkhid $ name <> ": Invalid ltkhid"
|
|
||||||
bnid <- decodeKeyHashidE bnkhid $ name <> ": Invalid bnkhid"
|
|
||||||
return $ Right (shr, rp, ltid, bnid)
|
|
||||||
_ -> throwE $ name <> ": not a bundle route"
|
_ -> throwE $ name <> ": not a bundle route"
|
||||||
else return $ Right u
|
else return $ Right u
|
||||||
|
|
||||||
getRemoteTicketByURI
|
|
||||||
:: MonadIO m
|
|
||||||
=> ObjURI URIMode
|
|
||||||
-> ExceptT Text (ReaderT SqlBackend m)
|
|
||||||
(Either
|
|
||||||
Text
|
|
||||||
( Entity Instance
|
|
||||||
, Entity RemoteObject
|
|
||||||
, Entity RemoteTicket
|
|
||||||
, Entity TicketAuthorRemote
|
|
||||||
, Entity TicketContextLocal
|
|
||||||
, Either (Entity TicketProjectLocal) (Entity TicketRepoLocal)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
getRemoteTicketByURI (ObjURI h lu) = adapt $ do
|
|
||||||
ei@(Entity iid _) <- do
|
|
||||||
mei <- lift $ getBy $ UniqueInstance h
|
|
||||||
fromMaybeE mei $ Right "Instance not known"
|
|
||||||
ero@(Entity roid _) <- do
|
|
||||||
mero <- lift $ getBy $ UniqueRemoteObject iid lu
|
|
||||||
fromMaybeE mero $ Right "Remote object not known"
|
|
||||||
ert@(Entity _ rt) <- do
|
|
||||||
mert <- lift $ getBy $ UniqueRemoteTicketIdent roid
|
|
||||||
fromMaybeE mert $ Right "Not a known RemoteTicket"
|
|
||||||
etar@(Entity _ tar) <- do
|
|
||||||
metar <- lift $ getEntity $ remoteTicketTicket rt
|
|
||||||
fromMaybeE metar $ Left "RT's TAR not found in DB"
|
|
||||||
etcl@(Entity tclid _) <- do
|
|
||||||
metcl <- lift $ getEntity $ ticketAuthorRemoteTicket tar
|
|
||||||
fromMaybeE metcl $ Left "TAR's TCL not found in DB"
|
|
||||||
ctx <- do
|
|
||||||
metjl <- lift $ getBy $ UniqueTicketProjectLocal tclid
|
|
||||||
metrl <- lift $ getBy $ UniqueTicketRepoLocal tclid
|
|
||||||
case (metjl, metrl) of
|
|
||||||
(Nothing, Nothing) -> throwE $ Left "TCL has neither TJL nor TRL"
|
|
||||||
(Just j, Nothing) -> return $ Left j
|
|
||||||
(Nothing, Just r) -> return $ Right r
|
|
||||||
(Just _, Just _) -> throwE $ Left "TCL has both TJL and TRL"
|
|
||||||
return (ei, ero, ert, etar, etcl, ctx)
|
|
||||||
where
|
|
||||||
adapt m = ExceptT $ adapt' <$> runExceptT m
|
|
||||||
where
|
|
||||||
adapt' (Left (Left e)) = Left e
|
|
||||||
adapt' (Left (Right e)) = Right $ Left e
|
|
||||||
adapt' (Right x) = Right $ Right x
|
|
||||||
|
|
||||||
checkDepAndTarget
|
checkDepAndTarget
|
||||||
:: (MonadSite m, SiteEnv m ~ App)
|
:: (MonadSite m, SiteEnv m ~ App)
|
||||||
=> TicketDependency URIMode
|
=> TicketDependency URIMode
|
||||||
|
@ -985,13 +708,13 @@ checkDepAndTarget
|
||||||
(parseLocalActor route)
|
(parseLocalActor route)
|
||||||
"Offer local target isn't an actor route"
|
"Offer local target isn't an actor route"
|
||||||
else return $ Right u
|
else return $ Right u
|
||||||
checkParentAndTarget (Left wi) (Left la) =
|
checkParentAndTarget (Left wi) (Left la) = do
|
||||||
unless (workItemActor wi == la) $
|
la' <-
|
||||||
|
case wi of
|
||||||
|
WorkItemTicket did _ -> LocalActorDeck <$> encodeKeyHashid did
|
||||||
|
WorkItemCloth lid _ -> LocalActorLoom <$> encodeKeyHashid lid
|
||||||
|
unless (la' == la) $
|
||||||
throwE "Parent and target mismatch"
|
throwE "Parent and target mismatch"
|
||||||
where
|
|
||||||
workItemActor (WorkItemSharerTicket shr _ _) = LocalActorSharer shr
|
|
||||||
workItemActor (WorkItemProjectTicket shr prj _) = LocalActorProject shr prj
|
|
||||||
workItemActor (WorkItemRepoProposal shr rp _) = LocalActorRepo shr rp
|
|
||||||
checkParentAndTarget (Left _) (Right _) = throwE "Local parent but remote target"
|
checkParentAndTarget (Left _) (Right _) = throwE "Local parent but remote target"
|
||||||
checkParentAndTarget (Right _) (Left _) = throwE "Local target but remote parent"
|
checkParentAndTarget (Right _) (Left _) = throwE "Local target but remote parent"
|
||||||
checkParentAndTarget (Right _) (Right _) = return ()
|
checkParentAndTarget (Right _) (Right _) = return ()
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{- This file is part of Vervis.
|
{- This file is part of Vervis.
|
||||||
-
|
-
|
||||||
- Written in 2016, 2019 by fr33domlover <fr33domlover@riseup.net>.
|
- Written in 2016, 2019, 2022 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.
|
||||||
-
|
-
|
||||||
|
@ -43,16 +43,19 @@ import Vervis.Model
|
||||||
import Vervis.Model.Ident
|
import Vervis.Model.Ident
|
||||||
import Yesod.RenderSource
|
import Yesod.RenderSource
|
||||||
import Vervis.Settings (widgetFile)
|
import Vervis.Settings (widgetFile)
|
||||||
import Vervis.Widget.Sharer
|
import Vervis.Widget.Person
|
||||||
|
|
||||||
actorLinkW :: MessageTreeNodeAuthor -> Widget
|
actorLinkW :: MessageTreeNodeAuthor -> Widget
|
||||||
actorLinkW actor = $(widgetFile "widget/actor-link")
|
actorLinkW actor = do
|
||||||
|
hashPerson <- getEncodeKeyHashid
|
||||||
|
$(widgetFile "widget/actor-link")
|
||||||
where
|
where
|
||||||
shortURI h (LocalURI p) = renderAuthority h <> p
|
shortURI h (LocalURI p) = renderAuthority h <> p
|
||||||
|
|
||||||
messageW
|
messageW
|
||||||
:: UTCTime -> MessageTreeNode -> (MessageId -> Route App) -> Widget
|
:: UTCTime -> MessageTreeNode -> (MessageId -> Route App) -> Widget
|
||||||
messageW now (MessageTreeNode msgid msg author) reply = do
|
messageW now (MessageTreeNode msgid msg author) reply = do
|
||||||
|
hashPerson <- getEncodeKeyHashid
|
||||||
encodeHid <- getEncodeKeyHashid
|
encodeHid <- getEncodeKeyHashid
|
||||||
let showTime =
|
let showTime =
|
||||||
showEventTime .
|
showEventTime .
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{- This file is part of Vervis.
|
{- This file is part of Vervis.
|
||||||
-
|
-
|
||||||
- Written in 2016, 2019 by fr33domlover <fr33domlover@riseup.net>.
|
- Written in 2016, 2019, 2022 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,9 +13,9 @@
|
||||||
- <http://creativecommons.org/publicdomain/zero/1.0/>.
|
- <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
-}
|
-}
|
||||||
|
|
||||||
module Vervis.Widget.Sharer
|
module Vervis.Widget.Person
|
||||||
( sharerLinkW
|
( personLinkW
|
||||||
, sharerLinkFedW
|
, personLinkFedW
|
||||||
, followW
|
, followW
|
||||||
, personNavW
|
, personNavW
|
||||||
)
|
)
|
||||||
|
@ -29,6 +29,7 @@ import Yesod.Persist.Core
|
||||||
|
|
||||||
import Network.FedURI
|
import Network.FedURI
|
||||||
import Yesod.Auth.Unverified
|
import Yesod.Auth.Unverified
|
||||||
|
import Yesod.Hashids
|
||||||
|
|
||||||
import Database.Persist.Local
|
import Database.Persist.Local
|
||||||
|
|
||||||
|
@ -38,19 +39,19 @@ import Vervis.Model.Ident
|
||||||
import Vervis.Settings
|
import Vervis.Settings
|
||||||
import Vervis.Widget
|
import Vervis.Widget
|
||||||
|
|
||||||
sharerLinkW :: Sharer -> Widget
|
personLinkW :: Entity Person -> Actor -> Widget
|
||||||
sharerLinkW sharer =
|
personLinkW (Entity personID person) actor = do
|
||||||
|
personHash <- encodeKeyHashid personID
|
||||||
[whamlet|
|
[whamlet|
|
||||||
<a href=@{SharerR $ sharerIdent sharer}>
|
<a href=@{PersonR personHash}>
|
||||||
$maybe name <- sharerName sharer
|
#{actorName actor} ~#{username2text $ personUsername person}
|
||||||
#{name}
|
|
||||||
$nothing
|
|
||||||
#{shr2text $ sharerIdent sharer}
|
|
||||||
|]
|
|]
|
||||||
|
|
||||||
sharerLinkFedW :: Either Sharer (Instance, RemoteObject, RemoteActor) -> Widget
|
personLinkFedW
|
||||||
sharerLinkFedW (Left sharer) = sharerLinkW sharer
|
:: Either (Entity Person, Actor) (Instance, RemoteObject, RemoteActor)
|
||||||
sharerLinkFedW (Right (inztance, object, actor)) =
|
-> Widget
|
||||||
|
personLinkFedW (Left (ep, a)) = personLinkW ep a
|
||||||
|
personLinkFedW (Right (inztance, object, actor)) =
|
||||||
[whamlet|
|
[whamlet|
|
||||||
<a href="#{renderObjURI uActor}">
|
<a href="#{renderObjURI uActor}">
|
||||||
$maybe name <- remoteActorName actor
|
$maybe name <- remoteActorName actor
|
||||||
|
@ -61,16 +62,18 @@ sharerLinkFedW (Right (inztance, object, actor)) =
|
||||||
where
|
where
|
||||||
uActor = ObjURI (instanceHost inztance) (remoteObjectIdent object)
|
uActor = ObjURI (instanceHost inztance) (remoteObjectIdent object)
|
||||||
|
|
||||||
followW :: Route App -> Route App -> AppDB FollowerSetId -> Widget
|
followW :: Route App -> Route App -> FollowerSetId -> Widget
|
||||||
followW followRoute unfollowRoute getFsid = do
|
followW followRoute unfollowRoute fsid = do
|
||||||
mpid <- maybeVerifiedAuthId
|
maybeUser <- maybeVerifiedAuth
|
||||||
for_ mpid $ \ pid -> do
|
for_ maybeUser $ \ (Entity _ user) -> do
|
||||||
mfollow <- handlerToWidget $ runDB $ do
|
mfollow <-
|
||||||
fsid <- getFsid
|
handlerToWidget $ runDB $
|
||||||
getValBy $ UniqueFollow pid fsid
|
getBy $ UniqueFollow (personActor user) fsid
|
||||||
case mfollow of
|
case mfollow of
|
||||||
Nothing -> buttonW POST "Follow" followRoute
|
Nothing -> buttonW POST "Follow" followRoute
|
||||||
Just _ -> buttonW POST "Unfollow" unfollowRoute
|
Just _ -> buttonW POST "Unfollow" unfollowRoute
|
||||||
|
|
||||||
personNavW :: ShrIdent -> Widget
|
personNavW :: Entity Person -> Widget
|
||||||
personNavW shr = $(widgetFile "person/widget/nav")
|
personNavW (Entity personID person) = do
|
||||||
|
personHash <- encodeKeyHashid personID
|
||||||
|
$(widgetFile "person/widget/nav")
|
|
@ -31,19 +31,22 @@ 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 Yesod.Hashids
|
||||||
|
|
||||||
import Data.Patch.Local (Hunk (..))
|
import Data.Patch.Local (Hunk (..))
|
||||||
|
|
||||||
import Vervis.Changes
|
import Vervis.Changes
|
||||||
import Vervis.Foundation
|
import Vervis.Foundation
|
||||||
|
import Vervis.Model
|
||||||
import Vervis.Model.Ident
|
import Vervis.Model.Ident
|
||||||
import Vervis.Settings (widgetFile, appDiffContextLines)
|
import Vervis.Settings (widgetFile, appDiffContextLines)
|
||||||
import Vervis.Style
|
import Vervis.Style
|
||||||
|
|
||||||
refSelectW :: ShrIdent -> RpIdent -> Set Text -> Set Text -> Widget
|
refSelectW :: KeyHashid Repo -> Set Text -> Set Text -> Widget
|
||||||
refSelectW shar repo branches tags = $(widgetFile "repo/widget/ref-select")
|
refSelectW hash branches tags = $(widgetFile "repo/widget/ref-select")
|
||||||
|
|
||||||
changesW :: Foldable f => ShrIdent -> RpIdent -> f LogEntry -> Widget
|
changesW :: Foldable f => KeyHashid Repo -> f LogEntry -> Widget
|
||||||
changesW shr rp entries = $(widgetFile "repo/widget/changes")
|
changesW hash entries = $(widgetFile "repo/widget/changes")
|
||||||
|
|
||||||
numberHunk :: Int -> Int -> Hunk -> (Int, Int, [(Bool, Int, Text)])
|
numberHunk :: Int -> Int -> Hunk -> (Int, Int, [(Bool, Int, Text)])
|
||||||
numberHunk startOld startNew hunk = j $ i ((startOld, startNew), []) hunk
|
numberHunk startOld startNew hunk = j $ i ((startOld, startNew), []) hunk
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{- This file is part of Vervis.
|
{- This file is part of Vervis.
|
||||||
-
|
-
|
||||||
- Written in 2020 by fr33domlover <fr33domlover@riseup.net>.
|
- Written in 2020, 2022 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.
|
||||||
-
|
-
|
||||||
|
@ -14,15 +14,16 @@
|
||||||
-}
|
-}
|
||||||
|
|
||||||
module Vervis.WorkItem
|
module Vervis.WorkItem
|
||||||
( WorkItemDetail (..)
|
(
|
||||||
, getWorkItemAuthorDetail
|
-- WorkItemDetail (..)
|
||||||
, askWorkItemFollowers
|
--, getWorkItemAuthorDetail
|
||||||
, contextAudience
|
askWorkItemFollowers
|
||||||
, authorAudience
|
--, contextAudience
|
||||||
, parseTicketContext
|
--, authorAudience
|
||||||
, getRemoteContextHttp
|
--, parseTicketContext
|
||||||
, getWorkItemDetail
|
--, getRemoteContextHttp
|
||||||
, WorkItemTarget (..)
|
--, getWorkItemDetail
|
||||||
|
--, WorkItemTarget (..)
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
|
@ -41,6 +42,7 @@ import Database.Persist.Sql
|
||||||
|
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
|
|
||||||
|
import Development.PatchMediaType
|
||||||
import Network.FedURI
|
import Network.FedURI
|
||||||
import Web.ActivityPub
|
import Web.ActivityPub
|
||||||
import Yesod.ActivityPub
|
import Yesod.ActivityPub
|
||||||
|
@ -52,15 +54,15 @@ import qualified Web.ActivityPub as AP
|
||||||
|
|
||||||
import Control.Monad.Trans.Except.Local
|
import Control.Monad.Trans.Except.Local
|
||||||
|
|
||||||
import Vervis.ActivityPub.Recipient
|
import Vervis.Cloth
|
||||||
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 Development.PatchMediaType
|
import Vervis.Recipient
|
||||||
import Vervis.Patch
|
|
||||||
import Vervis.Ticket
|
import Vervis.Ticket
|
||||||
|
|
||||||
|
{-
|
||||||
data WorkItemDetail = WorkItemDetail
|
data WorkItemDetail = WorkItemDetail
|
||||||
{ widIdent :: Either (WorkItem, LocalTicketId) (FedURI, LocalURI)
|
{ widIdent :: Either (WorkItem, LocalTicketId) (FedURI, LocalURI)
|
||||||
, widContext :: Either (Either (ShrIdent, PrjIdent) (ShrIdent, RpIdent)) (FedURI, Host, Maybe LocalURI, Maybe LocalURI)
|
, widContext :: Either (Either (ShrIdent, PrjIdent) (ShrIdent, RpIdent)) (FedURI, Host, Maybe LocalURI, Maybe LocalURI)
|
||||||
|
@ -85,19 +87,22 @@ getWorkItemAuthorDetail =
|
||||||
i <- getJust $ remoteObjectInstance ro
|
i <- getJust $ remoteObjectInstance ro
|
||||||
return (i, ro)
|
return (i, ro)
|
||||||
)
|
)
|
||||||
|
-}
|
||||||
|
|
||||||
askWorkItemFollowers
|
askWorkItemFollowers
|
||||||
:: (MonadSite m, YesodHashids (SiteEnv m))
|
:: (MonadSite m, YesodHashids (SiteEnv m)) => m (WorkItem -> LocalStage)
|
||||||
=> m (WorkItem -> LocalPersonCollection)
|
|
||||||
askWorkItemFollowers = do
|
askWorkItemFollowers = do
|
||||||
hashTALID <- getEncodeKeyHashid
|
hashDeck <- getEncodeKeyHashid
|
||||||
hashLTID <- getEncodeKeyHashid
|
hashLoom <- getEncodeKeyHashid
|
||||||
let workItemFollowers (WorkItemSharerTicket shr talid False) = LocalPersonCollectionSharerTicketFollowers shr $ hashTALID talid
|
hashTicket <- getEncodeKeyHashid
|
||||||
workItemFollowers (WorkItemSharerTicket shr talid True) = LocalPersonCollectionSharerProposalFollowers shr $ hashTALID talid
|
hashCloth <- getEncodeKeyHashid
|
||||||
workItemFollowers (WorkItemProjectTicket shr prj ltid) = LocalPersonCollectionProjectTicketFollowers shr prj $ hashLTID ltid
|
let workItemFollowers (WorkItemTicket deck ticket) =
|
||||||
workItemFollowers (WorkItemRepoProposal shr rp ltid) = LocalPersonCollectionRepoProposalFollowers shr rp $ hashLTID ltid
|
LocalStageTicketFollowers (hashDeck deck) (hashTicket ticket)
|
||||||
|
workItemFollowers (WorkItemCloth loom cloth) =
|
||||||
|
LocalStageClothFollowers (hashLoom loom) (hashCloth cloth)
|
||||||
return workItemFollowers
|
return workItemFollowers
|
||||||
|
|
||||||
|
{-
|
||||||
contextAudience
|
contextAudience
|
||||||
:: Either
|
:: Either
|
||||||
(Either (ShrIdent, PrjIdent) (ShrIdent, RpIdent))
|
(Either (ShrIdent, PrjIdent) (ShrIdent, RpIdent))
|
||||||
|
@ -249,3 +254,4 @@ getWorkItemDetail name v = do
|
||||||
data WorkItemTarget
|
data WorkItemTarget
|
||||||
= WITProject ShrIdent PrjIdent
|
= WITProject ShrIdent PrjIdent
|
||||||
| WITRepo ShrIdent RpIdent (Maybe Text) PatchMediaType (NonEmpty Text)
|
| WITRepo ShrIdent RpIdent (Maybe Text) PatchMediaType (NonEmpty Text)
|
||||||
|
-}
|
||||||
|
|
|
@ -451,7 +451,7 @@ instance ActivityPub Actor where
|
||||||
|
|
||||||
data Repo u = Repo
|
data Repo u = Repo
|
||||||
{ repoActor :: Actor u
|
{ repoActor :: Actor u
|
||||||
, repoTeam :: LocalURI
|
, repoTeam :: Maybe LocalURI
|
||||||
, repoVcs :: VersionControlSystem
|
, repoVcs :: VersionControlSystem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -463,16 +463,16 @@ instance ActivityPub Repo where
|
||||||
fail "Actor type isn't Repository"
|
fail "Actor type isn't Repository"
|
||||||
fmap (h,) $
|
fmap (h,) $
|
||||||
Repo a
|
Repo a
|
||||||
<$> withAuthorityO h (o .:| "team")
|
<$> withAuthorityMaybeO h (o .:|? "team")
|
||||||
<*> o .: "versionControlSystem"
|
<*> o .: "versionControlSystem"
|
||||||
toSeries authority (Repo actor team vcs)
|
toSeries authority (Repo actor team vcs)
|
||||||
= toSeries authority actor
|
= toSeries authority actor
|
||||||
<> "team" .= ObjURI authority team
|
<> "team" .= (ObjURI authority <$> team)
|
||||||
<> "versionControlSystem" .= vcs
|
<> "versionControlSystem" .= vcs
|
||||||
|
|
||||||
data TicketTracker u = TicketTracker
|
data TicketTracker u = TicketTracker
|
||||||
{ ticketTrackerActor :: Actor u
|
{ ticketTrackerActor :: Actor u
|
||||||
, ticketTrackerTeam :: LocalURI
|
, ticketTrackerTeam :: Maybe LocalURI
|
||||||
}
|
}
|
||||||
|
|
||||||
instance ActivityPub TicketTracker where
|
instance ActivityPub TicketTracker where
|
||||||
|
@ -483,10 +483,10 @@ instance ActivityPub TicketTracker where
|
||||||
fail "Actor type isn't TicketTracker"
|
fail "Actor type isn't TicketTracker"
|
||||||
fmap (h,) $
|
fmap (h,) $
|
||||||
TicketTracker a
|
TicketTracker a
|
||||||
<$> withAuthorityO h (o .:| "team")
|
<$> withAuthorityMaybeO h (o .:|? "team")
|
||||||
toSeries authority (TicketTracker actor team)
|
toSeries authority (TicketTracker actor team)
|
||||||
= toSeries authority actor
|
= toSeries authority actor
|
||||||
<> "team" .= ObjURI authority team
|
<> "team" .= (ObjURI authority <$> team)
|
||||||
|
|
||||||
data CollectionType = CollectionTypeUnordered | CollectionTypeOrdered
|
data CollectionType = CollectionTypeUnordered | CollectionTypeOrdered
|
||||||
|
|
||||||
|
@ -1085,7 +1085,7 @@ encodeTicketLocal
|
||||||
|
|
||||||
data MergeRequest u = MergeRequest
|
data MergeRequest u = MergeRequest
|
||||||
{ mrOrigin :: Maybe (ObjURI u)
|
{ mrOrigin :: Maybe (ObjURI u)
|
||||||
, mrTarget :: LocalURI
|
, mrTarget :: Either LocalURI (Branch u)
|
||||||
, mrBundle :: Either (ObjURI u) (Authority u, Bundle u)
|
, mrBundle :: Either (ObjURI u) (Authority u, Bundle u)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1097,12 +1097,16 @@ instance ActivityPub MergeRequest where
|
||||||
unless (typ == ("Offer" :: Text)) $
|
unless (typ == ("Offer" :: Text)) $
|
||||||
fail "type isn't Offer"
|
fail "type isn't Offer"
|
||||||
|
|
||||||
ObjURI a target <- o .: "target"
|
target <- o .:+ "target"
|
||||||
|
let (a, target') =
|
||||||
|
case target of
|
||||||
|
Left (ObjURI h lu) -> (h, Left lu)
|
||||||
|
Right (Doc h branch) -> (h, Right branch)
|
||||||
|
|
||||||
fmap (a,) $
|
fmap (a,) $
|
||||||
MergeRequest
|
MergeRequest
|
||||||
<$> o .:? "origin"
|
<$> o .:? "origin"
|
||||||
<*> pure target
|
<*> pure target'
|
||||||
<*> (second fromDoc . toEither <$> o .: "object")
|
<*> (second fromDoc . toEither <$> o .: "object")
|
||||||
where
|
where
|
||||||
fromDoc (Doc h v) = (h, v)
|
fromDoc (Doc h v) = (h, v)
|
||||||
|
@ -1110,7 +1114,7 @@ instance ActivityPub MergeRequest where
|
||||||
toSeries h (MergeRequest morigin target bundle)
|
toSeries h (MergeRequest morigin target bundle)
|
||||||
= "type" .= ("Offer" :: Text)
|
= "type" .= ("Offer" :: Text)
|
||||||
<> "origin" .=? morigin
|
<> "origin" .=? morigin
|
||||||
<> "target" .= ObjURI h target
|
<> "target" .=+ bimap (ObjURI h) (Doc h) target
|
||||||
<> "object" .= fromEither (second (uncurry Doc) bundle)
|
<> "object" .= fromEither (second (uncurry Doc) bundle)
|
||||||
|
|
||||||
data Ticket u = Ticket
|
data Ticket u = Ticket
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{- This file is part of Vervis.
|
{- This file is part of Vervis.
|
||||||
-
|
-
|
||||||
- Written in 2019, 2020 by fr33domlover <fr33domlover@riseup.net>.
|
- Written in 2019, 2020, 2022 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.
|
||||||
-
|
-
|
||||||
|
@ -17,9 +17,12 @@ module Yesod.Hashids
|
||||||
( YesodHashids (..)
|
( YesodHashids (..)
|
||||||
, KeyHashid ()
|
, KeyHashid ()
|
||||||
, keyHashidText
|
, keyHashidText
|
||||||
|
|
||||||
, encodeKeyHashidPure
|
, encodeKeyHashidPure
|
||||||
, getEncodeKeyHashid
|
, getEncodeKeyHashid
|
||||||
, encodeKeyHashid
|
, encodeKeyHashid
|
||||||
|
|
||||||
|
, decodeKeyHashidPure
|
||||||
, decodeKeyHashid
|
, decodeKeyHashid
|
||||||
, decodeKeyHashidF
|
, decodeKeyHashidF
|
||||||
, decodeKeyHashidM
|
, decodeKeyHashidM
|
||||||
|
@ -83,6 +86,14 @@ encodeKeyHashid k = do
|
||||||
enc <- getEncodeKeyHashid
|
enc <- getEncodeKeyHashid
|
||||||
return $ enc k
|
return $ enc k
|
||||||
|
|
||||||
|
decodeKeyHashidPure
|
||||||
|
:: ToBackendKey SqlBackend record
|
||||||
|
=> HashidsContext
|
||||||
|
-> KeyHashid record
|
||||||
|
-> Maybe (Key record)
|
||||||
|
decodeKeyHashidPure ctx (KeyHashid t) =
|
||||||
|
fmap toSqlKey $ decodeInt64 ctx $ encodeUtf8 t
|
||||||
|
|
||||||
decodeKeyHashid
|
decodeKeyHashid
|
||||||
:: ( MonadSite m
|
:: ( MonadSite m
|
||||||
, YesodHashids (SiteEnv m)
|
, YesodHashids (SiteEnv m)
|
||||||
|
@ -90,9 +101,9 @@ decodeKeyHashid
|
||||||
)
|
)
|
||||||
=> KeyHashid record
|
=> KeyHashid record
|
||||||
-> m (Maybe (Key record))
|
-> m (Maybe (Key record))
|
||||||
decodeKeyHashid (KeyHashid t) = do
|
decodeKeyHashid khid = do
|
||||||
ctx <- asksSite siteHashidsContext
|
ctx <- asksSite siteHashidsContext
|
||||||
return $ fmap toSqlKey $ decodeInt64 ctx $ encodeUtf8 t
|
return $ decodeKeyHashidPure ctx khid
|
||||||
|
|
||||||
decodeKeyHashidF
|
decodeKeyHashidF
|
||||||
:: ( MonadFail m
|
:: ( MonadFail m
|
||||||
|
|
|
@ -29,7 +29,9 @@ $# <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
<p>
|
<p>
|
||||||
Vervis is a web application written in the Haskell programming language and
|
Vervis is a web application written in the Haskell programming language and
|
||||||
the Yesod web framework. It's free as in freedom, under AGPLv3. It's being
|
the Yesod web framework. It's free as in freedom, under AGPLv3. It's being
|
||||||
developed by fr33domlover, who can be found under this nickname on Freenode
|
developed by fr33domlover, who can be found under this nickname on
|
||||||
|
<a href="https://libera.chat">
|
||||||
|
Libera Chat
|
||||||
in the #peers channel.
|
in the #peers channel.
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
@ -55,53 +57,37 @@ $# <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
and
|
and
|
||||||
<a href="https://socialhub.activitypub.rocks/c/software/forgefed">forum
|
<a href="https://socialhub.activitypub.rocks/c/software/forgefed">forum
|
||||||
|
|
||||||
<h2>Repos
|
|
||||||
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<th>Sharer
|
|
||||||
<th>Project
|
|
||||||
<th>Repo
|
|
||||||
<th>VCS
|
|
||||||
<th>Last change
|
|
||||||
$forall (sharer, mproj, repo, vcs, ago) <- rowsRepo
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<a href=@{SharerR sharer}>#{shr2text sharer}
|
|
||||||
<td>
|
|
||||||
$maybe proj <- mproj
|
|
||||||
<a href=@{ProjectR sharer proj}>#{prj2text proj}
|
|
||||||
$nothing
|
|
||||||
(none)
|
|
||||||
<td>
|
|
||||||
<a href=@{RepoR sharer repo}>#{rp2text repo}
|
|
||||||
<td>
|
|
||||||
$case vcs
|
|
||||||
$of VCSDarcs
|
|
||||||
Darcs
|
|
||||||
$of VCSGit
|
|
||||||
Git
|
|
||||||
<td>
|
|
||||||
$maybe t <- ago
|
|
||||||
#{t}
|
|
||||||
$nothing
|
|
||||||
Error
|
|
||||||
|
|
||||||
<h2>Projects without repos
|
|
||||||
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<th>Sharer
|
|
||||||
<th>Project
|
|
||||||
$forall (E.Value sharer, E.Value project) <- rowsProject
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<a href=@{SharerR sharer}>#{shr2text sharer}
|
|
||||||
<td>
|
|
||||||
<a href=@{ProjectR sharer project}>#{prj2text project}
|
|
||||||
|
|
||||||
<h2>People
|
<h2>People
|
||||||
|
|
||||||
<p>
|
<ul>
|
||||||
See
|
$forall (Entity personID person, Entity _ actor) <- people
|
||||||
<a href=@{PeopleR}>people</a>.
|
<a href=@{PersonR $ hashPerson personID}>
|
||||||
|
~#{username2text $ personUsername person} #{actorName actor}
|
||||||
|
|
||||||
|
<h2>Groups
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
$forall (Entity groupID _, Entity _ actor) <- groups
|
||||||
|
<a href=@{GroupR $ hashGroup groupID}>
|
||||||
|
&#{keyHashidText $ hashGroup groupID} #{actorName actor}
|
||||||
|
|
||||||
|
<h2>Repos
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
$forall (Entity repoID _, Entity _ actor) <- repos
|
||||||
|
<a href=@{RepoR $ hashRepo repoID}>
|
||||||
|
^#{keyHashidText $ hashRepo repoID} #{actorName actor}
|
||||||
|
|
||||||
|
<h2>Decks
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
$forall (Entity deckID _, Entity _ actor) <- decks
|
||||||
|
<a href=@{DeckR $ hashDeck deckID}>
|
||||||
|
=#{keyHashidText $ hashDeck deckID} #{actorName actor}
|
||||||
|
|
||||||
|
<h2>Looms
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
$forall (Entity loomID _, Entity _ actor) <- looms
|
||||||
|
<a href=@{LoomR $ hashLoom loomID}>
|
||||||
|
+#{keyHashidText $ hashLoom loomID} #{actorName actor}
|
|
@ -13,7 +13,7 @@ $# with this software. If not, see
|
||||||
$# <http://creativecommons.org/publicdomain/zero/1.0/>.
|
$# <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
|
|
||||||
<header>
|
<header>
|
||||||
$maybe (Entity _pid person, verified, sharer, unread) <- mperson
|
$maybe (Entity _ person, hash, verified, unread) <- mperson
|
||||||
<div>
|
<div>
|
||||||
$if verified
|
$if verified
|
||||||
<span>
|
<span>
|
||||||
|
@ -21,19 +21,19 @@ $# <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
<span .username>#{personLogin person}</span>]
|
<span .username>#{personLogin person}</span>]
|
||||||
$if unread > 0
|
$if unread > 0
|
||||||
<span>
|
<span>
|
||||||
<a href=@{NotificationsR $ sharerIdent sharer}>
|
<a href=@{NotificationsR}>
|
||||||
🔔#{unread}
|
🔔#{unread}
|
||||||
<span>
|
<span>
|
||||||
<a href=@{SharerInboxR $ sharerIdent sharer}>
|
<a href=@{PersonInboxR hash}>
|
||||||
[📥 Inbox]
|
[📥 Inbox]
|
||||||
<span>
|
<span>
|
||||||
<a href=@{SharerOutboxR $ sharerIdent sharer}>
|
<a href=@{PersonOutboxR hash}>
|
||||||
[📤 Outbox]
|
[📤 Outbox]
|
||||||
<span>
|
<span>
|
||||||
<a href=@{SharerFollowersR $ sharerIdent sharer}>
|
<a href=@{PersonFollowersR hash}>
|
||||||
[🐤 Followers]
|
[🐤 Followers]
|
||||||
<span>
|
<span>
|
||||||
<a href=@{SharerFollowingR $ sharerIdent sharer}>
|
<a href=@{PersonFollowingR hash}>
|
||||||
[🐔 Following]
|
[🐔 Following]
|
||||||
<span>
|
<span>
|
||||||
<a href=@{BrowseR}>
|
<a href=@{BrowseR}>
|
||||||
|
@ -52,7 +52,7 @@ $# <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
it. Or <a href=@{AuthR LogoutR}>Log out.
|
it. Or <a href=@{AuthR LogoutR}>Log out.
|
||||||
$if unread > 0
|
$if unread > 0
|
||||||
<span>
|
<span>
|
||||||
<a href=@{NotificationsR $ sharerIdent sharer}>
|
<a href=@{NotificationsR}>
|
||||||
🔔#{unread}
|
🔔#{unread}
|
||||||
$nothing
|
$nothing
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
$# This file is part of Vervis.
|
$# This file is part of Vervis.
|
||||||
$#
|
$#
|
||||||
$# Written in 2016, 2019 by fr33domlover <fr33domlover@riseup.net>.
|
$# Written in 2016, 2019, 2022 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.
|
||||||
$#
|
$#
|
||||||
|
@ -18,8 +18,8 @@ $# <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
^{actorLinkW author}
|
^{actorLinkW author}
|
||||||
<span .time>
|
<span .time>
|
||||||
$case author
|
$case author
|
||||||
$of MessageTreeNodeLocal lmid s
|
$of MessageTreeNodeLocal lmid pid
|
||||||
<a href=@{MessageR (sharerIdent s) (encodeHid lmid)}>
|
<a href=@{PersonMessageR (hashPerson pid) (encodeHid lmid)}>
|
||||||
#{showTime $ messageCreated msg}
|
#{showTime $ messageCreated msg}
|
||||||
$of MessageTreeNodeRemote h luMsg _luAuthor _mname
|
$of MessageTreeNodeRemote h luMsg _luAuthor _mname
|
||||||
<a href="#{renderObjURI $ ObjURI h luMsg}"}>
|
<a href="#{renderObjURI $ ObjURI h luMsg}"}>
|
||||||
|
|
|
@ -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, 2022 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.
|
||||||
$#
|
$#
|
||||||
|
@ -16,6 +16,6 @@ $# <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
|
|
||||||
^{followButton}
|
^{followButton}
|
||||||
|
|
||||||
<p>#{personAbout person}
|
<p>#{actorDesc actor}
|
||||||
|
|
||||||
^{personNavW shr}
|
^{personNavW ep}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
$# This file is part of Vervis.
|
$# This file is part of Vervis.
|
||||||
$#
|
$#
|
||||||
$# Written in 2019 by fr33domlover <fr33domlover@riseup.net>.
|
$# Written in 2019, 2022 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.
|
||||||
$#
|
$#
|
||||||
|
@ -16,7 +16,7 @@ $if null notifications
|
||||||
<p>
|
<p>
|
||||||
Nothing new here :-)
|
Nothing new here :-)
|
||||||
$else
|
$else
|
||||||
<form method=POST action=@{NotificationsR shr} enctype=#{enctypeAll}>
|
<form method=POST action=@{NotificationsR} enctype=#{enctypeAll}>
|
||||||
^{widgetAll}
|
^{widgetAll}
|
||||||
<div class="submit">
|
<div class="submit">
|
||||||
<input type="submit" value="Mark all as read">
|
<input type="submit" value="Mark all as read">
|
||||||
|
@ -37,7 +37,7 @@ $else
|
||||||
$nothing
|
$nothing
|
||||||
^{renderPrettyJSONSkylighting obj}
|
^{renderPrettyJSONSkylighting obj}
|
||||||
|
|
||||||
<form method=POST action=@{NotificationsR shr} enctype=#{enctype}>
|
<form method=POST action=@{NotificationsR} enctype=#{enctype}>
|
||||||
^{widget}
|
^{widget}
|
||||||
<div class="submit">
|
<div class="submit">
|
||||||
<input type="submit" value="Mark as read">
|
<input type="submit" value="Mark as read">
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
$# This file is part of Vervis.
|
$# This file is part of Vervis.
|
||||||
$#
|
$#
|
||||||
$# Written in 2019, 2020 by fr33domlover <fr33domlover@riseup.net>.
|
$# Written in 2019, 2020, 2022 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.
|
||||||
$#
|
$#
|
||||||
|
@ -15,30 +15,27 @@ $# <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
<div>
|
<div>
|
||||||
<span>
|
<span>
|
||||||
[[ 🧙
|
[[ 🧙
|
||||||
<a href=@{SharerR shr}>
|
<a href=@{PersonR personHash}>
|
||||||
#{shr2text shr}
|
~#{username2text $ personUsername person}
|
||||||
]] ::
|
]] ::
|
||||||
<span>
|
<span>
|
||||||
<a href=@{SharerInboxR shr}>
|
<a href=@{PersonInboxR personHash}>
|
||||||
[📥 Inbox]
|
[📥 Inbox]
|
||||||
<span>
|
<span>
|
||||||
<a href=@{SharerOutboxR shr}>
|
<a href=@{PersonOutboxR personHash}>
|
||||||
[📤 Outbox]
|
[📤 Outbox]
|
||||||
<span>
|
<span>
|
||||||
<a href=@{SharerFollowersR shr}>
|
<a href=@{PersonFollowersR personHash}>
|
||||||
[🐤 Followers]
|
[🐤 Followers]
|
||||||
<span>
|
<span>
|
||||||
<a href=@{SharerFollowingR shr}>
|
<a href=@{PersonFollowingR personHash}>
|
||||||
[🐔 Following]
|
[🐔 Following]
|
||||||
<span>
|
<span>
|
||||||
<a href=@{ProjectsR shr}>
|
<a href="">
|
||||||
[🏗 Projects]
|
[🏗 Projects]
|
||||||
<span>
|
<span>
|
||||||
<a href=@{ReposR shr}>
|
<a href="">
|
||||||
[🗃 Repositories]
|
[🗃 Repositories]
|
||||||
<span>
|
<span>
|
||||||
<a href=@{WorkflowsR shr}>
|
<a href="">
|
||||||
[🔁 Workflows]
|
[🔁 Workflows]
|
||||||
<span>
|
|
||||||
<a href=@{SharerTicketsR shr}>
|
|
||||||
[🐛 Tickets]
|
|
||||||
|
|
|
@ -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, 2022 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.
|
||||||
$#
|
$#
|
||||||
|
@ -21,32 +21,3 @@ $# <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
<li>
|
<li>
|
||||||
<a href=@{PublishR}>
|
<a href=@{PublishR}>
|
||||||
Publish an activity
|
Publish an activity
|
||||||
<li>
|
|
||||||
<p>Projects:
|
|
||||||
<ul>
|
|
||||||
$forall project <- projects
|
|
||||||
<li>
|
|
||||||
<a href=@{ProjectR ident project}>#{prj2text project}
|
|
||||||
<li>
|
|
||||||
<a href=@{ProjectNewR ident}>Create new…
|
|
||||||
|
|
||||||
<li>
|
|
||||||
<p>Standalone repos:
|
|
||||||
<ul>
|
|
||||||
$forall repo <- repos
|
|
||||||
<li>
|
|
||||||
<a href=@{RepoR ident repo}>#{rp2text repo}
|
|
||||||
<li>
|
|
||||||
<a href=@{RepoNewR ident}>Create new…
|
|
||||||
|
|
||||||
<li>
|
|
||||||
<a href=@{SharerInboxR ident}>Inbox
|
|
||||||
|
|
||||||
<li>
|
|
||||||
<a href=@{KeysR}>SSH keys
|
|
||||||
|
|
||||||
<li>
|
|
||||||
<a href=@{ProjectRolesR ident}>Roles
|
|
||||||
|
|
||||||
<li>
|
|
||||||
<a href=@{ClaimRequestsPersonR}>Ticket claim requests
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
$# This file is part of Vervis.
|
$# This file is part of Vervis.
|
||||||
$#
|
$#
|
||||||
$# Written in 2016, 2019 by fr33domlover <fr33domlover@riseup.net>.
|
$# Written in 2016, 2019, 2022 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.
|
||||||
$#
|
$#
|
||||||
|
@ -14,15 +14,12 @@ $# <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<span>
|
<span>
|
||||||
$maybe name <- projectName project
|
#{actorName actor}
|
||||||
#{name}
|
-
|
||||||
$nothing
|
<span>
|
||||||
#{prj2text proj}
|
#{actorDesc actor}
|
||||||
$maybe desc <- projectDesc project
|
|
||||||
-
|
|
||||||
<span>#{desc}
|
|
||||||
|
|
||||||
^{personNavW shar}
|
^{personNavW $ Entity deckID deck}
|
||||||
|
|
||||||
^{projectNavW project workflow wsharer shar proj}
|
^{projectNavW project workflow wsharer shar proj}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
$# This file is part of Vervis.
|
$# This file is part of Vervis.
|
||||||
$#
|
$#
|
||||||
$# Written in 2018, 2019 by fr33domlover <fr33domlover@riseup.net>.
|
$# Written in 2018, 2019, 2022 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.
|
||||||
$#
|
$#
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
$# This file is part of Vervis.
|
$# This file is part of Vervis.
|
||||||
$#
|
$#
|
||||||
$# Written in 2016, 2018, 2020 by fr33domlover <fr33domlover@riseup.net>.
|
$# Written in 2016, 2018, 2020, 2022 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.
|
||||||
$#
|
$#
|
||||||
|
@ -18,11 +18,11 @@ $# <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
<th>Hash
|
<th>Hash
|
||||||
<th>Message
|
<th>Message
|
||||||
<th>Time
|
<th>Time
|
||||||
$forall LogEntry author hash message (_, time) <- entries
|
$forall LogEntry author changeHash message (_, time) <- entries
|
||||||
<tr>
|
<tr>
|
||||||
<td>#{author}
|
<td>#{author}
|
||||||
<td .hash>
|
<td .hash>
|
||||||
<a href=@{RepoCommitR shr rp hash}>
|
<a href=@{RepoCommitR hash changeHash}>
|
||||||
#{T.take 10 hash}
|
#{T.take 10 changeHash}
|
||||||
<td>#{message}
|
<td>#{message}
|
||||||
<td>#{time}
|
<td>#{time}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
$# This file is part of Vervis.
|
$# This file is part of Vervis.
|
||||||
$#
|
$#
|
||||||
$# Written in 2016 by fr33domlover <fr33domlover@riseup.net>.
|
$# Written in 2016, 2022 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.
|
||||||
$#
|
$#
|
||||||
|
@ -17,11 +17,11 @@ $# <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
<ul>
|
<ul>
|
||||||
$forall branch <- branches
|
$forall branch <- branches
|
||||||
<li>
|
<li>
|
||||||
<a href=@{RepoSourceR shar repo [branch]}>#{branch}
|
<a href=@{RepoBranchSourceR hash branch []}>#{branch}
|
||||||
|
|
||||||
<h2>Tags
|
<h2>Tags
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
$forall tag <- tags
|
$forall tag <- tags
|
||||||
<li>
|
<li>
|
||||||
<a href=@{RepoSourceR shar repo [tag]}>#{tag}
|
<a href=@{RepoBranchSourceR hash tag []}>#{tag}
|
||||||
|
|
|
@ -13,14 +13,11 @@ $# with this software. If not, see
|
||||||
$# <http://creativecommons.org/publicdomain/zero/1.0/>.
|
$# <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
|
|
||||||
$case actor
|
$case actor
|
||||||
$of MessageTreeNodeLocal _lmid s
|
$of MessageTreeNodeLocal _lmid pid
|
||||||
<a href=@{SharerR $ sharerIdent s}>
|
<a href=@{PersonR $ hashPerson pid}>
|
||||||
$maybe name <- sharerName s
|
~#{keyHashidText $ hashPerson pid}
|
||||||
#{name}
|
|
||||||
$nothing
|
|
||||||
#{shr2text $ sharerIdent s}
|
|
||||||
<span>
|
<span>
|
||||||
./s/#{shr2text $ sharerIdent s}
|
./people/#{keyHashidText $ hashPerson pid}
|
||||||
$of MessageTreeNodeRemote h _luMsg luAuthor mname
|
$of MessageTreeNodeRemote h _luMsg luAuthor mname
|
||||||
<a href="#{renderObjURI $ ObjURI h luAuthor}">
|
<a href="#{renderObjURI $ ObjURI h luAuthor}">
|
||||||
$maybe name <- mname
|
$maybe name <- mname
|
||||||
|
|
401
th/models
401
th/models
|
@ -13,9 +13,9 @@
|
||||||
-- with this software. If not, see
|
-- with this software. If not, see
|
||||||
-- <http://creativecommons.org/publicdomain/zero/1.0/>.
|
-- <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-- ========================================================================= --
|
||||||
-- Instances
|
-- Remote Object
|
||||||
-------------------------------------------------------------------------------
|
-- ========================================================================= --
|
||||||
|
|
||||||
Instance
|
Instance
|
||||||
host Host
|
host Host
|
||||||
|
@ -28,8 +28,81 @@ RemoteObject
|
||||||
|
|
||||||
UniqueRemoteObject instance ident
|
UniqueRemoteObject instance ident
|
||||||
|
|
||||||
|
RemoteActivity
|
||||||
|
ident RemoteObjectId
|
||||||
|
content PersistJSONObject
|
||||||
|
received UTCTime
|
||||||
|
|
||||||
|
UniqueRemoteActivity ident
|
||||||
|
|
||||||
|
UnfetchedRemoteActor
|
||||||
|
ident RemoteObjectId
|
||||||
|
since UTCTime Maybe
|
||||||
|
|
||||||
|
UniqueUnfetchedRemoteActor ident
|
||||||
|
|
||||||
|
RemoteActor
|
||||||
|
ident RemoteObjectId
|
||||||
|
name Text Maybe
|
||||||
|
inbox LocalURI
|
||||||
|
followers LocalURI Maybe
|
||||||
|
errorSince UTCTime Maybe
|
||||||
|
|
||||||
|
UniqueRemoteActor ident
|
||||||
|
|
||||||
|
RemoteCollection
|
||||||
|
ident RemoteObjectId
|
||||||
|
|
||||||
|
UniqueRemoteCollection ident
|
||||||
|
|
||||||
|
-- ========================================================================= --
|
||||||
|
-- Local Actor
|
||||||
|
-- ========================================================================= --
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
-- People
|
-- Outbox
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Outbox
|
||||||
|
|
||||||
|
OutboxItem
|
||||||
|
outbox OutboxId
|
||||||
|
activity PersistJSONObject
|
||||||
|
published UTCTime
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Inbox
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Inbox
|
||||||
|
|
||||||
|
InboxItem
|
||||||
|
unread Bool
|
||||||
|
|
||||||
|
InboxItemLocal
|
||||||
|
inbox InboxId
|
||||||
|
activity OutboxItemId
|
||||||
|
item InboxItemId
|
||||||
|
|
||||||
|
UniqueInboxItemLocal inbox activity
|
||||||
|
UniqueInboxItemLocalItem item
|
||||||
|
|
||||||
|
InboxItemRemote
|
||||||
|
inbox InboxId
|
||||||
|
activity RemoteActivityId
|
||||||
|
item InboxItemId
|
||||||
|
|
||||||
|
UniqueInboxItemRemote inbox activity
|
||||||
|
UniqueInboxItemRemoteItem item
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Followers
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
FollowerSet
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- Actors
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
Actor
|
Actor
|
||||||
|
@ -44,15 +117,8 @@ Actor
|
||||||
UniqueActorOutbox outbox
|
UniqueActorOutbox outbox
|
||||||
UniqueActorFollowers followers
|
UniqueActorFollowers followers
|
||||||
|
|
||||||
Sharer
|
|
||||||
ident ShrIdent
|
|
||||||
name Text Maybe
|
|
||||||
created UTCTime
|
|
||||||
|
|
||||||
UniqueSharer ident
|
|
||||||
|
|
||||||
Person
|
Person
|
||||||
ident SharerId
|
username Username
|
||||||
login Text
|
login Text
|
||||||
passphraseHash ByteString
|
passphraseHash ByteString
|
||||||
email EmailAddress
|
email EmailAddress
|
||||||
|
@ -61,52 +127,17 @@ Person
|
||||||
verifiedKeyCreated UTCTime
|
verifiedKeyCreated UTCTime
|
||||||
resetPassKey Text
|
resetPassKey Text
|
||||||
resetPassKeyCreated UTCTime
|
resetPassKeyCreated UTCTime
|
||||||
about Text
|
actor ActorId
|
||||||
inbox InboxId
|
-- reviewFollow Bool
|
||||||
outbox OutboxId
|
|
||||||
followers FollowerSetId
|
|
||||||
|
|
||||||
UniquePersonIdent ident
|
UniquePersonUsername username
|
||||||
UniquePersonLogin login
|
UniquePersonLogin login
|
||||||
UniquePersonEmail email
|
UniquePersonEmail email
|
||||||
UniquePersonInbox inbox
|
UniquePersonActor actor
|
||||||
UniquePersonOutbox outbox
|
|
||||||
UniquePersonFollowers followers
|
|
||||||
|
|
||||||
Outbox
|
-- ========================================================================= --
|
||||||
|
-- Delivery
|
||||||
OutboxItem
|
-- ========================================================================= --
|
||||||
outbox OutboxId
|
|
||||||
activity PersistJSONObject
|
|
||||||
published UTCTime
|
|
||||||
|
|
||||||
Inbox
|
|
||||||
|
|
||||||
InboxItem
|
|
||||||
unread Bool
|
|
||||||
|
|
||||||
InboxItemLocal
|
|
||||||
inbox InboxId
|
|
||||||
activity OutboxItemId
|
|
||||||
item InboxItemId
|
|
||||||
|
|
||||||
UniqueInboxItemLocal inbox activity
|
|
||||||
UniqueInboxItemLocalItem item
|
|
||||||
|
|
||||||
RemoteActivity
|
|
||||||
ident RemoteObjectId
|
|
||||||
content PersistJSONObject
|
|
||||||
received UTCTime
|
|
||||||
|
|
||||||
UniqueRemoteActivity ident
|
|
||||||
|
|
||||||
InboxItemRemote
|
|
||||||
inbox InboxId
|
|
||||||
activity RemoteActivityId
|
|
||||||
item InboxItemId
|
|
||||||
|
|
||||||
UniqueInboxItemRemote inbox activity
|
|
||||||
UniqueInboxItemRemoteItem item
|
|
||||||
|
|
||||||
UnlinkedDelivery
|
UnlinkedDelivery
|
||||||
recipient UnfetchedRemoteActorId
|
recipient UnfetchedRemoteActorId
|
||||||
|
@ -133,17 +164,17 @@ Forwarding
|
||||||
|
|
||||||
UniqueForwarding recipient activity
|
UniqueForwarding recipient activity
|
||||||
|
|
||||||
ForwarderSharer
|
ForwarderPerson
|
||||||
task ForwardingId
|
task ForwardingId
|
||||||
sender SharerId
|
sender PersonId
|
||||||
|
|
||||||
UniqueForwarderSharer task
|
UniqueForwarderPerson task
|
||||||
|
|
||||||
ForwarderProject
|
ForwarderGroup
|
||||||
task ForwardingId
|
task ForwardingId
|
||||||
sender ProjectId
|
sender GroupId
|
||||||
|
|
||||||
UniqueForwarderProject task
|
UniqueForwarderGroup task
|
||||||
|
|
||||||
ForwarderRepo
|
ForwarderRepo
|
||||||
task ForwardingId
|
task ForwardingId
|
||||||
|
@ -151,6 +182,25 @@ ForwarderRepo
|
||||||
|
|
||||||
UniqueForwarderRepo task
|
UniqueForwarderRepo task
|
||||||
|
|
||||||
|
ForwarderLoom
|
||||||
|
task ForwardingId
|
||||||
|
sender LoomId
|
||||||
|
|
||||||
|
UniqueForwarderLoom task
|
||||||
|
|
||||||
|
ForwarderDeck
|
||||||
|
task ForwardingId
|
||||||
|
sender DeckId
|
||||||
|
|
||||||
|
UniqueForwarderDeck task
|
||||||
|
|
||||||
|
-- ========================================================================= --
|
||||||
|
-- ========================================================================= --
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- People
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
VerifKey
|
VerifKey
|
||||||
ident LocalRefURI
|
ident LocalRefURI
|
||||||
instance InstanceId
|
instance InstanceId
|
||||||
|
@ -166,25 +216,12 @@ VerifKeySharedUsage
|
||||||
|
|
||||||
UniqueVerifKeySharedUsage key user
|
UniqueVerifKeySharedUsage key user
|
||||||
|
|
||||||
UnfetchedRemoteActor
|
--RemoteFollowRequest
|
||||||
ident RemoteObjectId
|
-- actor RemoteActorId
|
||||||
since UTCTime Maybe
|
-- target PersonId
|
||||||
|
--
|
||||||
UniqueUnfetchedRemoteActor ident
|
-- UniqueRemoteFollowRequest actor target
|
||||||
|
--
|
||||||
RemoteActor
|
|
||||||
ident RemoteObjectId
|
|
||||||
name Text Maybe
|
|
||||||
inbox LocalURI
|
|
||||||
followers LocalURI Maybe
|
|
||||||
errorSince UTCTime Maybe
|
|
||||||
|
|
||||||
UniqueRemoteActor ident
|
|
||||||
|
|
||||||
RemoteCollection
|
|
||||||
ident RemoteObjectId
|
|
||||||
|
|
||||||
UniqueRemoteCollection ident
|
|
||||||
|
|
||||||
FollowRemoteRequest
|
FollowRemoteRequest
|
||||||
person PersonId
|
person PersonId
|
||||||
|
@ -197,27 +234,31 @@ FollowRemoteRequest
|
||||||
UniqueFollowRemoteRequestActivity activity
|
UniqueFollowRemoteRequestActivity activity
|
||||||
|
|
||||||
FollowRemote
|
FollowRemote
|
||||||
person PersonId
|
actor ActorId
|
||||||
recip RemoteActorId -- actor managing the followed object
|
recip RemoteActorId -- actor managing the followed object
|
||||||
target FedURI -- the followed object
|
target FedURI -- the followed object
|
||||||
public Bool
|
public Bool
|
||||||
follow OutboxItemId
|
follow OutboxItemId
|
||||||
accept RemoteActivityId
|
accept RemoteActivityId
|
||||||
|
|
||||||
UniqueFollowRemote person target
|
UniqueFollowRemote actor target
|
||||||
UniqueFollowRemoteFollow follow
|
UniqueFollowRemoteFollow follow
|
||||||
UniqueFollowRemoteAccept accept
|
UniqueFollowRemoteAccept accept
|
||||||
|
|
||||||
FollowerSet
|
--FollowRequest
|
||||||
|
-- person PersonId
|
||||||
|
-- target FollowerSetId
|
||||||
|
--
|
||||||
|
-- UniqueFollowRequest person target
|
||||||
|
|
||||||
Follow
|
Follow
|
||||||
person PersonId
|
actor ActorId
|
||||||
target FollowerSetId
|
target FollowerSetId
|
||||||
public Bool
|
public Bool
|
||||||
follow OutboxItemId
|
follow OutboxItemId
|
||||||
accept OutboxItemId
|
accept OutboxItemId
|
||||||
|
|
||||||
UniqueFollow person target
|
UniqueFollow actor target
|
||||||
UniqueFollowFollow follow
|
UniqueFollowFollow follow
|
||||||
UniqueFollowAccept accept
|
UniqueFollowAccept accept
|
||||||
|
|
||||||
|
@ -241,9 +282,9 @@ SshKey
|
||||||
UniqueSshKey person ident
|
UniqueSshKey person ident
|
||||||
|
|
||||||
Group
|
Group
|
||||||
ident SharerId
|
actor ActorId
|
||||||
|
|
||||||
UniqueGroup ident
|
UniqueGroupActor actor
|
||||||
|
|
||||||
GroupMember
|
GroupMember
|
||||||
person PersonId
|
person PersonId
|
||||||
|
@ -253,13 +294,12 @@ GroupMember
|
||||||
|
|
||||||
UniqueGroupMember person group
|
UniqueGroupMember person group
|
||||||
|
|
||||||
|
-- I'm removing the 'sharer' field, so all roles are now public for everyone to
|
||||||
|
-- use! This is temporary, until I figure out a sane plan for federated roles
|
||||||
Role
|
Role
|
||||||
ident RlIdent
|
ident RlIdent
|
||||||
sharer SharerId
|
|
||||||
desc Text
|
desc Text
|
||||||
|
|
||||||
UniqueRole sharer ident
|
|
||||||
|
|
||||||
RoleInherit
|
RoleInherit
|
||||||
parent RoleId
|
parent RoleId
|
||||||
child RoleId
|
child RoleId
|
||||||
|
@ -276,12 +316,8 @@ RoleAccess
|
||||||
-- Projects
|
-- Projects
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
Project
|
Deck
|
||||||
actor ActorId
|
actor ActorId
|
||||||
ident PrjIdent
|
|
||||||
sharer SharerId
|
|
||||||
name Text Maybe
|
|
||||||
desc Text Maybe
|
|
||||||
workflow WorkflowId
|
workflow WorkflowId
|
||||||
nextTicket Int
|
nextTicket Int
|
||||||
wiki RepoId Maybe
|
wiki RepoId Maybe
|
||||||
|
@ -289,37 +325,40 @@ Project
|
||||||
collabAnon RoleId Maybe
|
collabAnon RoleId Maybe
|
||||||
create OutboxItemId
|
create OutboxItemId
|
||||||
|
|
||||||
UniqueProjectActor actor
|
UniqueDeckActor actor
|
||||||
UniqueProjectCreate create
|
UniqueDeckCreate create
|
||||||
UniqueProject ident sharer
|
|
||||||
|
Loom
|
||||||
|
nextTicket Int
|
||||||
|
actor ActorId
|
||||||
|
repo RepoId
|
||||||
|
create OutboxItemId
|
||||||
|
|
||||||
|
UniqueLoomActor actor
|
||||||
|
UniqueLoomRepo repo
|
||||||
|
UniqueLoomCreate create
|
||||||
|
|
||||||
Repo
|
Repo
|
||||||
ident RpIdent
|
|
||||||
sharer SharerId
|
|
||||||
vcs VersionControlSystem
|
vcs VersionControlSystem
|
||||||
project ProjectId Maybe
|
project DeckId Maybe
|
||||||
desc Text Maybe
|
|
||||||
mainBranch Text
|
mainBranch Text
|
||||||
collabUser RoleId Maybe
|
collabUser RoleId Maybe
|
||||||
collabAnon RoleId Maybe
|
collabAnon RoleId Maybe
|
||||||
inbox InboxId
|
actor ActorId
|
||||||
outbox OutboxId
|
create OutboxItemId
|
||||||
followers FollowerSetId
|
|
||||||
|
|
||||||
UniqueRepo ident sharer
|
UniqueRepoActor actor
|
||||||
UniqueRepoInbox inbox
|
UniqueRepoCreate create
|
||||||
UniqueRepoOutbox outbox
|
|
||||||
UniqueRepoFollowers followers
|
|
||||||
|
|
||||||
|
-- I removed the 'sharer' field so Workflows don't specify who controls them
|
||||||
|
-- For now there's no way to create new ones, and what's already in the DB can
|
||||||
|
-- be publicly experimented with, until I make a plan for federated workflows
|
||||||
Workflow
|
Workflow
|
||||||
sharer SharerId
|
|
||||||
ident WflIdent
|
ident WflIdent
|
||||||
name Text Maybe
|
name Text Maybe
|
||||||
desc Text Maybe
|
desc Text Maybe
|
||||||
scope WorkflowScope
|
scope WorkflowScope
|
||||||
|
|
||||||
UniqueWorkflow sharer ident
|
|
||||||
|
|
||||||
WorkflowField
|
WorkflowField
|
||||||
workflow WorkflowId
|
workflow WorkflowId
|
||||||
ident FldIdent
|
ident FldIdent
|
||||||
|
@ -377,69 +416,37 @@ Ticket
|
||||||
title Text -- HTML
|
title Text -- HTML
|
||||||
source Text -- Pandoc Markdown
|
source Text -- Pandoc Markdown
|
||||||
description Text -- HTML
|
description Text -- HTML
|
||||||
assignee PersonId Maybe
|
|
||||||
status TicketStatus
|
status TicketStatus
|
||||||
|
discuss DiscussionId
|
||||||
|
followers FollowerSetId
|
||||||
|
accept OutboxItemId
|
||||||
|
|
||||||
-- UniqueTicket project number
|
-- UniqueTicket project number
|
||||||
|
UniqueTicketDiscuss discuss
|
||||||
|
UniqueTicketFollowers followers
|
||||||
|
UniqueTicketAccept accept
|
||||||
|
|
||||||
LocalTicket
|
TicketAssignee
|
||||||
ticket TicketId
|
ticket TicketId
|
||||||
discuss DiscussionId
|
person PersonId
|
||||||
followers FollowerSetId
|
|
||||||
|
|
||||||
UniqueLocalTicket ticket
|
UniqueTicketAssignee ticket person
|
||||||
UniqueLocalTicketDiscussion discuss
|
|
||||||
UniqueLocalTicketFollowers followers
|
|
||||||
|
|
||||||
RemoteTicket
|
TicketDeck
|
||||||
ticket TicketAuthorRemoteId
|
ticket TicketId
|
||||||
ident RemoteObjectId
|
deck DeckId
|
||||||
discuss RemoteDiscussionId
|
|
||||||
|
|
||||||
UniqueRemoteTicket ticket
|
UniqueTicketDeck ticket
|
||||||
UniqueRemoteTicketIdent ident
|
|
||||||
UniqueRemoteTicketDiscuss discuss
|
|
||||||
|
|
||||||
TicketContextLocal
|
TicketLoom
|
||||||
ticket TicketId
|
ticket TicketId
|
||||||
accept OutboxItemId
|
loom LoomId
|
||||||
|
branch Text Maybe
|
||||||
|
|
||||||
UniqueTicketContextLocal ticket
|
UniqueTicketLoom ticket
|
||||||
UniqueTicketContextLocalAccept accept
|
|
||||||
|
|
||||||
TicketProjectLocal
|
|
||||||
context TicketContextLocalId
|
|
||||||
project ProjectId
|
|
||||||
|
|
||||||
UniqueTicketProjectLocal context
|
|
||||||
|
|
||||||
TicketRepoLocal
|
|
||||||
context TicketContextLocalId
|
|
||||||
repo RepoId
|
|
||||||
branch Text Maybe
|
|
||||||
|
|
||||||
UniqueTicketRepoLocal context
|
|
||||||
|
|
||||||
TicketProjectRemote
|
|
||||||
ticket TicketAuthorLocalId
|
|
||||||
tracker RemoteActorId
|
|
||||||
project RemoteObjectId Maybe -- specify if not same as tracker
|
|
||||||
-- For MRs it may be either a remote repo or
|
|
||||||
-- a branch of it
|
|
||||||
|
|
||||||
UniqueTicketProjectRemote ticket
|
|
||||||
|
|
||||||
TicketProjectRemoteAccept
|
|
||||||
ticket TicketProjectRemoteId
|
|
||||||
activity RemoteActivityId
|
|
||||||
accept Bool
|
|
||||||
result LocalURI Maybe
|
|
||||||
|
|
||||||
UniqueTicketProjectRemoteAccept ticket
|
|
||||||
UniqueTicketProjectRemoteAcceptActivity activity
|
|
||||||
|
|
||||||
TicketAuthorLocal
|
TicketAuthorLocal
|
||||||
ticket LocalTicketId
|
ticket TicketId
|
||||||
author PersonId
|
author PersonId
|
||||||
open OutboxItemId
|
open OutboxItemId
|
||||||
|
|
||||||
|
@ -447,22 +454,15 @@ TicketAuthorLocal
|
||||||
UniqueTicketAuthorLocalOpen open
|
UniqueTicketAuthorLocalOpen open
|
||||||
|
|
||||||
TicketAuthorRemote
|
TicketAuthorRemote
|
||||||
ticket TicketContextLocalId
|
ticket TicketId
|
||||||
author RemoteActorId
|
author RemoteActorId
|
||||||
open RemoteActivityId
|
open RemoteActivityId
|
||||||
|
|
||||||
UniqueTicketAuthorRemote ticket
|
UniqueTicketAuthorRemote ticket
|
||||||
UniqueTicketAuthorRemoteOpen open
|
UniqueTicketAuthorRemoteOpen open
|
||||||
|
|
||||||
TicketUnderProject
|
|
||||||
project TicketContextLocalId
|
|
||||||
author TicketAuthorLocalId
|
|
||||||
|
|
||||||
UniqueTicketUnderProjectProject project
|
|
||||||
UniqueTicketUnderProjectAuthor author
|
|
||||||
|
|
||||||
Bundle
|
Bundle
|
||||||
ticket TicketId
|
ticket TicketLoomId
|
||||||
|
|
||||||
Patch
|
Patch
|
||||||
bundle BundleId
|
bundle BundleId
|
||||||
|
@ -470,28 +470,24 @@ Patch
|
||||||
type PatchMediaType
|
type PatchMediaType
|
||||||
content Text
|
content Text
|
||||||
|
|
||||||
TicketDependencyOffer
|
|
||||||
offer InboxItemId
|
|
||||||
child LocalTicketId
|
|
||||||
|
|
||||||
UniqueTicketDependencyOffer offer
|
|
||||||
|
|
||||||
RemoteTicketDependency
|
RemoteTicketDependency
|
||||||
ident RemoteObjectId
|
ident RemoteObjectId
|
||||||
child LocalTicketId
|
child TicketId
|
||||||
accept RemoteActivityId
|
accept RemoteActivityId
|
||||||
|
|
||||||
UniqueRemoteTicketDependency ident
|
UniqueRemoteTicketDependency ident
|
||||||
UniqueRemoteTicketDependencyAccept accept
|
UniqueRemoteTicketDependencyAccept accept
|
||||||
|
|
||||||
LocalTicketDependency
|
LocalTicketDependency
|
||||||
parent LocalTicketId
|
parent TicketId
|
||||||
created UTCTime
|
created UTCTime
|
||||||
accept OutboxItemId
|
accept OutboxItemId
|
||||||
|
|
||||||
|
UniqueLocalTicketDependencyAccept accept
|
||||||
|
|
||||||
TicketDependencyChildLocal
|
TicketDependencyChildLocal
|
||||||
dep LocalTicketDependencyId
|
dep LocalTicketDependencyId
|
||||||
child LocalTicketId
|
child TicketId
|
||||||
|
|
||||||
UniqueTicketDependencyChildLocal dep
|
UniqueTicketDependencyChildLocal dep
|
||||||
|
|
||||||
|
@ -526,7 +522,7 @@ TicketClaimRequest
|
||||||
UniqueTicketClaimRequest person ticket
|
UniqueTicketClaimRequest person ticket
|
||||||
|
|
||||||
TicketResolve
|
TicketResolve
|
||||||
ticket LocalTicketId
|
ticket TicketId
|
||||||
accept OutboxItemId
|
accept OutboxItemId
|
||||||
|
|
||||||
UniqueTicketResolve ticket
|
UniqueTicketResolve ticket
|
||||||
|
@ -604,11 +600,17 @@ CollabTopicLocalRepo
|
||||||
|
|
||||||
UniqueCollabTopicLocalRepo collab
|
UniqueCollabTopicLocalRepo collab
|
||||||
|
|
||||||
CollabTopicLocalProject
|
CollabTopicLocalDeck
|
||||||
collab CollabId
|
collab CollabId
|
||||||
project ProjectId
|
deck DeckId
|
||||||
|
|
||||||
UniqueCollabTopicLocalProject collab
|
UniqueCollabTopicLocalDeck collab
|
||||||
|
|
||||||
|
CollabTopicLocalLoom
|
||||||
|
collab CollabId
|
||||||
|
loom LoomId
|
||||||
|
|
||||||
|
UniqueCollabTopicLocalLoom collab
|
||||||
|
|
||||||
CollabTopicRemote
|
CollabTopicRemote
|
||||||
collab CollabId
|
collab CollabId
|
||||||
|
@ -654,3 +656,24 @@ CollabFulfillsLocalTopicCreation
|
||||||
collab CollabId
|
collab CollabId
|
||||||
|
|
||||||
UniqueCollabFulfillsLocalTopicCreation collab
|
UniqueCollabFulfillsLocalTopicCreation collab
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--RepoRemoteCollab
|
||||||
|
-- repo RepoId
|
||||||
|
-- collab RemoteActorId
|
||||||
|
-- role RoleId Maybe
|
||||||
|
-- cap Text
|
||||||
|
--
|
||||||
|
-- UniqueRepoRemoteCollab repo collab
|
||||||
|
-- UniqueRepoRemoteCollabCap cap
|
||||||
|
--
|
||||||
|
--ProjectRemoteCollab
|
||||||
|
-- project DeckId
|
||||||
|
-- collab RemoteActorId
|
||||||
|
-- role RoleId Maybe
|
||||||
|
-- cap Text
|
||||||
|
--
|
||||||
|
-- UniqueProjectRemoteCollab project person
|
||||||
|
-- UniqueProjectRemoteCollabCap cap
|
||||||
|
|
373
th/routes
373
th/routes
|
@ -17,205 +17,260 @@
|
||||||
-- Yesod misc
|
-- Yesod misc
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
|
||||||
/static StaticR Static appStatic
|
-- /highlight/#Text/style.css HighlightStyleR GET
|
||||||
/favicon.svg FaviconSvgR GET
|
|
||||||
/favicon.png FaviconPngR GET
|
|
||||||
/robots.txt RobotsR GET
|
|
||||||
|
|
||||||
/highlight/#Text/style.css HighlightStyleR GET
|
|
||||||
|
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
-- Internal
|
-- Internal
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
|
||||||
/post-receive PostReceiveR POST
|
|
||||||
|
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
-- Federation
|
-- Federation
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
|
||||||
/publish PublishR GET POST
|
|
||||||
/inbox InboxDebugR GET
|
|
||||||
/akey1 ActorKey1R GET
|
|
||||||
/akey2 ActorKey2R GET
|
|
||||||
|
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
-- Current user
|
-- Current user
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
|
||||||
/ HomeR GET
|
-- /k KeysR GET POST
|
||||||
|
-- /k/!new KeyNewR GET
|
||||||
|
-- /k/#KyIdent KeyR GET DELETE POST
|
||||||
|
|
||||||
/auth/!resend ResendVerifyEmailR GET
|
-- /cr ClaimRequestsPersonR GET
|
||||||
/auth AuthR Auth getAuth
|
|
||||||
/oauth DvaraR Dvara getDvara
|
|
||||||
|
|
||||||
/k KeysR GET POST
|
|
||||||
/k/!new KeyNewR GET
|
|
||||||
/k/#KyIdent KeyR GET DELETE POST
|
|
||||||
|
|
||||||
/cr ClaimRequestsPersonR GET
|
|
||||||
|
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
-- People
|
-- People
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
|
||||||
/s SharersR GET
|
-- /g/!new GroupNewR GET
|
||||||
/s/#ShrIdent SharerR GET
|
-- /g/#ShrIdent/m GroupMembersR GET POST
|
||||||
/s/#ShrIdent/inbox SharerInboxR GET POST
|
-- /g/#ShrIdent/m/!new GroupMemberNewR GET
|
||||||
/s/#ShrIdent/notifications NotificationsR GET POST
|
-- /g/#ShrIdent/m/#ShrIdent GroupMemberR GET DELETE POST
|
||||||
/s/#ShrIdent/outbox SharerOutboxR GET POST
|
|
||||||
/s/#ShrIdent/outbox/#OutboxItemKeyHashid SharerOutboxItemR GET
|
|
||||||
/s/#ShrIdent/followers SharerFollowersR GET
|
|
||||||
/s/#ShrIdent/following SharerFollowingR GET
|
|
||||||
/s/#ShrIdent/follow SharerFollowR POST
|
|
||||||
/s/#ShrIdent/unfollow SharerUnfollowR POST
|
|
||||||
|
|
||||||
/s/#ShrIdent/k/#SshKeyKeyHashid SshKeyR GET
|
-- /s/#ShrIdent/pr ProjectRolesR GET POST
|
||||||
|
-- /s/#ShrIdent/pr/!new ProjectRoleNewR GET
|
||||||
/p PeopleR GET
|
-- /s/#ShrIdent/pr/#RlIdent ProjectRoleR GET DELETE POST
|
||||||
|
-- /s/#ShrIdent/pr/#RlIdent/a ProjectRoleOpsR GET POST
|
||||||
/g GroupsR GET POST
|
-- /s/#ShrIdent/pr/#RlIdent/a/!new ProjectRoleOpNewR GET
|
||||||
/g/!new GroupNewR GET
|
|
||||||
/g/#ShrIdent/m GroupMembersR GET POST
|
|
||||||
/g/#ShrIdent/m/!new GroupMemberNewR GET
|
|
||||||
/g/#ShrIdent/m/#ShrIdent GroupMemberR GET DELETE POST
|
|
||||||
|
|
||||||
/s/#ShrIdent/pr ProjectRolesR GET POST
|
|
||||||
/s/#ShrIdent/pr/!new ProjectRoleNewR GET
|
|
||||||
/s/#ShrIdent/pr/#RlIdent ProjectRoleR GET DELETE POST
|
|
||||||
/s/#ShrIdent/pr/#RlIdent/a ProjectRoleOpsR GET POST
|
|
||||||
/s/#ShrIdent/pr/#RlIdent/a/!new ProjectRoleOpNewR GET
|
|
||||||
|
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
-- Projects
|
-- Projects
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
|
||||||
/browse BrowseR GET
|
-- /s/#ShrIdent/r ReposR GET
|
||||||
|
|
||||||
/s/#ShrIdent/r ReposR GET POST
|
-- /s/#ShrIdent/p/#PrjIdent/tcr ClaimRequestsProjectR GET
|
||||||
/s/#ShrIdent/r/!new RepoNewR GET
|
|
||||||
/s/#ShrIdent/r/#RpIdent RepoR GET PUT DELETE POST
|
|
||||||
/s/#ShrIdent/r/#RpIdent/inbox RepoInboxR GET POST
|
|
||||||
/s/#ShrIdent/r/#RpIdent/outbox RepoOutboxR GET
|
|
||||||
/s/#ShrIdent/r/#RpIdent/outbox/#OutboxItemKeyHashid RepoOutboxItemR GET
|
|
||||||
/s/#ShrIdent/r/#RpIdent/team RepoTeamR GET
|
|
||||||
/s/#ShrIdent/r/#RpIdent/followers RepoFollowersR GET
|
|
||||||
/s/#ShrIdent/r/#RpIdent/edit RepoEditR GET
|
|
||||||
/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/c RepoHeadChangesR GET
|
|
||||||
/s/#ShrIdent/r/#RpIdent/b/#Text RepoBranchR GET
|
|
||||||
/s/#ShrIdent/r/#RpIdent/c/#Text RepoChangesR GET
|
|
||||||
/s/#ShrIdent/r/#RpIdent/p/#Text RepoCommitR GET
|
|
||||||
/s/#ShrIdent/r/#RpIdent/d RepoDevsR GET POST
|
|
||||||
/s/#ShrIdent/r/#RpIdent/d/!new RepoDevNewR GET
|
|
||||||
/s/#ShrIdent/r/#RpIdent/d/#ShrIdent RepoDevR GET DELETE POST
|
|
||||||
|
|
||||||
/s/#ShrIdent/r/#RpIdent/mr RepoProposalsR GET
|
|
||||||
|
|
||||||
/s/#ShrIdent/r/#RpIdent/mr/#LocalTicketKeyHashid RepoProposalR GET
|
|
||||||
/s/#ShrIdent/r/#RpIdent/mr/#LocalTicketKeyHashid/d RepoProposalDiscussionR GET
|
|
||||||
/s/#ShrIdent/r/#RpIdent/mr/#LocalTicketKeyHashid/deps RepoProposalDepsR GET
|
|
||||||
/s/#ShrIdent/r/#RpIdent/mr/#LocalTicketKeyHashid/rdeps RepoProposalReverseDepsR GET
|
|
||||||
/s/#ShrIdent/r/#RpIdent/mr/#LocalTicketKeyHashid/followers RepoProposalFollowersR GET
|
|
||||||
/s/#ShrIdent/r/#RpIdent/mr/#LocalTicketKeyHashid/events RepoProposalEventsR GET
|
|
||||||
|
|
||||||
/s/#ShrIdent/r/#RpIdent/mr/#LocalTicketKeyHashid/b/#BundleKeyHashid RepoProposalBundleR GET
|
|
||||||
/s/#ShrIdent/r/#RpIdent/mr/#LocalTicketKeyHashid/b/#BundleKeyHashid/pt/#PatchKeyHashid RepoProposalBundlePatchR GET
|
|
||||||
|
|
||||||
/s/#ShrIdent/r/#RpIdent/_darcs/+Texts DarcsDownloadR GET
|
|
||||||
|
|
||||||
/s/#ShrIdent/r/#RpIdent/info/refs GitRefDiscoverR GET
|
|
||||||
/s/#ShrIdent/r/#RpIdent/git-upload-pack GitUploadRequestR POST
|
|
||||||
|
|
||||||
/s/#ShrIdent/p ProjectsR GET POST
|
|
||||||
/s/#ShrIdent/p/!new ProjectNewR GET
|
|
||||||
/s/#ShrIdent/p/#PrjIdent ProjectR GET PUT POST
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/inbox ProjectInboxR GET POST
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/outbox ProjectOutboxR GET
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/outbox/#OutboxItemKeyHashid ProjectOutboxItemR GET
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/team ProjectTeamR GET
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/followers ProjectFollowersR GET
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/edit ProjectEditR GET
|
|
||||||
/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/!new ProjectDevNewR GET
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/d/#ShrIdent ProjectDevR GET DELETE POST
|
|
||||||
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/tcr ClaimRequestsProjectR GET
|
|
||||||
|
|
||||||
-- /w GlobalWorkflowsR GET POST
|
-- /w GlobalWorkflowsR GET POST
|
||||||
-- /w/!new GlobalWorkflowNewR GET
|
-- /w/!new GlobalWorkflowNewR GET
|
||||||
-- /w/#WflIdent GlobalWorkflowR GET DELETE POST
|
-- /w/#WflIdent GlobalWorkflowR GET DELETE POST
|
||||||
|
|
||||||
/s/#ShrIdent/w WorkflowsR GET POST
|
-- /s/#ShrIdent/w WorkflowsR GET POST
|
||||||
/s/#ShrIdent/w/!new WorkflowNewR GET
|
-- /s/#ShrIdent/w/!new WorkflowNewR GET
|
||||||
/s/#ShrIdent/w/#WflIdent WorkflowR GET DELETE POST
|
-- /s/#ShrIdent/w/#WflIdent WorkflowR GET DELETE POST
|
||||||
/s/#ShrIdent/w/#WflIdent/f WorkflowFieldsR GET POST
|
-- /s/#ShrIdent/w/#WflIdent/f WorkflowFieldsR GET POST
|
||||||
/s/#ShrIdent/w/#WflIdent/f/!new WorkflowFieldNewR GET
|
-- /s/#ShrIdent/w/#WflIdent/f/!new WorkflowFieldNewR GET
|
||||||
/s/#ShrIdent/w/#WflIdent/f/#FldIdent WorkflowFieldR GET DELETE POST
|
-- /s/#ShrIdent/w/#WflIdent/f/#FldIdent WorkflowFieldR GET DELETE POST
|
||||||
/s/#ShrIdent/w/#WflIdent/e WorkflowEnumsR GET POST
|
-- /s/#ShrIdent/w/#WflIdent/e WorkflowEnumsR GET POST
|
||||||
/s/#ShrIdent/w/#WflIdent/e/!new WorkflowEnumNewR GET
|
-- /s/#ShrIdent/w/#WflIdent/e/!new WorkflowEnumNewR GET
|
||||||
/s/#ShrIdent/w/#WflIdent/e/#EnmIdent WorkflowEnumR GET DELETE POST
|
-- /s/#ShrIdent/w/#WflIdent/e/#EnmIdent WorkflowEnumR GET DELETE POST
|
||||||
/s/#ShrIdent/w/#WflIdent/e/#EnmIdent/c WorkflowEnumCtorsR GET POST
|
-- /s/#ShrIdent/w/#WflIdent/e/#EnmIdent/c WorkflowEnumCtorsR GET POST
|
||||||
/s/#ShrIdent/w/#WflIdent/e/#EnmIdent/c/!new WorkflowEnumCtorNewR GET
|
-- /s/#ShrIdent/w/#WflIdent/e/#EnmIdent/c/!new WorkflowEnumCtorNewR GET
|
||||||
/s/#ShrIdent/w/#WflIdent/e/#EnmIdent/c/#Text WorkflowEnumCtorR PUT DELETE POST
|
-- /s/#ShrIdent/w/#WflIdent/e/#EnmIdent/c/#Text WorkflowEnumCtorR PUT DELETE POST
|
||||||
|
|
||||||
/s/#ShrIdent/m/#LocalMessageKeyHashid MessageR GET
|
-- /s/#ShrIdent/p/#PrjIdent/w/+Texts WikiPageR GET
|
||||||
|
|
||||||
/tdeps/#TicketDepKeyHashid TicketDepR GET
|
|
||||||
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/t ProjectTicketsR GET POST
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/t/!tree ProjectTicketTreeR GET
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/t/!new ProjectTicketNewR GET
|
|
||||||
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/t/#LocalTicketKeyHashid ProjectTicketR GET PUT DELETE POST
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/t/#LocalTicketKeyHashid/edit ProjectTicketEditR GET
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/t/#LocalTicketKeyHashid/accept ProjectTicketAcceptR POST
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/t/#LocalTicketKeyHashid/close ProjectTicketCloseR POST
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/t/#LocalTicketKeyHashid/open ProjectTicketOpenR POST
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/t/#LocalTicketKeyHashid/claim ProjectTicketClaimR POST
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/t/#LocalTicketKeyHashid/unclaim ProjectTicketUnclaimR POST
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/t/#LocalTicketKeyHashid/assign ProjectTicketAssignR GET POST
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/t/#LocalTicketKeyHashid/unassign ProjectTicketUnassignR POST
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/t/#LocalTicketKeyHashid/follow ProjectTicketFollowR POST
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/t/#LocalTicketKeyHashid/unfollow ProjectTicketUnfollowR POST
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/t/#LocalTicketKeyHashid/cr ClaimRequestsTicketR GET POST
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/t/#LocalTicketKeyHashid/cr/new ClaimRequestNewR GET
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/t/#LocalTicketKeyHashid/d ProjectTicketDiscussionR GET POST
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/t/#LocalTicketKeyHashid/d/!reply ProjectTicketTopReplyR GET
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/t/#LocalTicketKeyHashid/d/#MessageKeyHashid ProjectTicketMessageR POST
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/t/#LocalTicketKeyHashid/d/#MessageKeyHashid/reply ProjectTicketReplyR GET
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/t/#LocalTicketKeyHashid/deps ProjectTicketDepsR GET POST
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/t/#LocalTicketKeyHashid/deps/!new ProjectTicketDepNewR GET
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/t/#LocalTicketKeyHashid/deps/#LocalTicketKeyHashid TicketDepOldR POST DELETE
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/t/#LocalTicketKeyHashid/rdeps ProjectTicketReverseDepsR GET
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/t/#LocalTicketKeyHashid/participants ProjectTicketParticipantsR GET
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/t/#LocalTicketKeyHashid/team ProjectTicketTeamR GET
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/t/#LocalTicketKeyHashid/events ProjectTicketEventsR GET
|
|
||||||
|
|
||||||
/s/#ShrIdent/t SharerTicketsR GET
|
|
||||||
|
|
||||||
/s/#ShrIdent/t/#TicketAuthorLocalKeyHashid SharerTicketR GET
|
|
||||||
/s/#ShrIdent/t/#TicketAuthorLocalKeyHashid/d SharerTicketDiscussionR GET
|
|
||||||
/s/#ShrIdent/t/#TicketAuthorLocalKeyHashid/deps SharerTicketDepsR GET
|
|
||||||
/s/#ShrIdent/t/#TicketAuthorLocalKeyHashid/rdeps SharerTicketReverseDepsR GET
|
|
||||||
/s/#ShrIdent/t/#TicketAuthorLocalKeyHashid/followers SharerTicketFollowersR GET
|
|
||||||
/s/#ShrIdent/t/#TicketAuthorLocalKeyHashid/team SharerTicketTeamR GET
|
|
||||||
/s/#ShrIdent/t/#TicketAuthorLocalKeyHashid/events SharerTicketEventsR GET
|
|
||||||
|
|
||||||
/s/#ShrIdent/mr SharerProposalsR GET
|
|
||||||
|
|
||||||
/s/#ShrIdent/mr/#TicketAuthorLocalKeyHashid SharerProposalR GET
|
|
||||||
/s/#ShrIdent/mr/#TicketAuthorLocalKeyHashid/d SharerProposalDiscussionR GET
|
|
||||||
/s/#ShrIdent/mr/#TicketAuthorLocalKeyHashid/deps SharerProposalDepsR GET
|
|
||||||
/s/#ShrIdent/mr/#TicketAuthorLocalKeyHashid/rdeps SharerProposalReverseDepsR GET
|
|
||||||
/s/#ShrIdent/mr/#TicketAuthorLocalKeyHashid/followers SharerProposalFollowersR GET
|
|
||||||
/s/#ShrIdent/mr/#TicketAuthorLocalKeyHashid/events SharerProposalEventsR GET
|
|
||||||
|
|
||||||
/s/#ShrIdent/mr/#TicketAuthorLocalKeyHashid/b/#BundleKeyHashid SharerProposalBundleR GET
|
|
||||||
/s/#ShrIdent/mr/#TicketAuthorLocalKeyHashid/b/#BundleKeyHashid/pt/#PatchKeyHashid SharerProposalBundlePatchR GET
|
|
||||||
|
|
||||||
/s/#ShrIdent/p/#PrjIdent/w/+Texts WikiPageR GET
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
-- New route structure
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
---- Static Files ------------------------------------------------------------
|
||||||
|
|
||||||
|
/static StaticR Static appStatic
|
||||||
|
/favicon.svg FaviconSvgR GET
|
||||||
|
/favicon.png FaviconPngR GET
|
||||||
|
/robots.txt RobotsR GET
|
||||||
|
|
||||||
|
---- Authentication ----------------------------------------------------------
|
||||||
|
|
||||||
|
/auth/!resend ResendVerifyEmailR GET
|
||||||
|
/auth AuthR Auth getAuth
|
||||||
|
/oauth DvaraR Dvara getDvara
|
||||||
|
/akey1 ActorKey1R GET
|
||||||
|
/akey2 ActorKey2R GET
|
||||||
|
|
||||||
|
---- Client ------------------------------------------------------------------
|
||||||
|
|
||||||
|
/ HomeR GET
|
||||||
|
/browse BrowseR GET
|
||||||
|
/notifications NotificationsR GET POST
|
||||||
|
/publish PublishR GET POST
|
||||||
|
/inbox InboxDebugR GET
|
||||||
|
|
||||||
|
---- Person ------------------------------------------------------------------
|
||||||
|
|
||||||
|
/people/#PersonKeyHashid PersonR GET
|
||||||
|
/people/#PersonKeyHashid/inbox PersonInboxR GET POST
|
||||||
|
/people/#PersonKeyHashid/outbox PersonOutboxR GET POST
|
||||||
|
/people/#PersonKeyHashid/outbox/#OutboxItemKeyHashid PersonOutboxItemR GET
|
||||||
|
/people/#PersonKeyHashid/followers PersonFollowersR GET
|
||||||
|
/people/#PersonKeyHashid/following PersonFollowingR GET
|
||||||
|
|
||||||
|
/people/#PersonKeyHashid/ssh-keys/#SshKeyKeyHashid SshKeyR GET
|
||||||
|
|
||||||
|
/people/#PersonKeyHashid/messages/#LocalMessageKeyHashid PersonMessageR GET
|
||||||
|
|
||||||
|
/people/#PersonKeyHashid/follow PersonFollowR POST
|
||||||
|
/people/#PersonKeyHashid/unfollow PersonUnfollowR POST
|
||||||
|
|
||||||
|
---- Group ------------------------------------------------------------------
|
||||||
|
|
||||||
|
/groups/#GroupKeyHashid GroupR GET
|
||||||
|
/groups/#GroupKeyHashid/inbox GroupInboxR GET POST
|
||||||
|
/groups/#GroupKeyHashid/outbox GroupOutboxR GET
|
||||||
|
/groups/#GroupKeyHashid/outbox/#OutboxItemKeyHashid GroupOutboxItemR GET
|
||||||
|
/groups/#GroupKeyHashid/followers GroupFollowersR GET
|
||||||
|
|
||||||
|
---- Repo --------------------------------------------------------------------
|
||||||
|
|
||||||
|
/repos/#RepoKeyHashid RepoR GET
|
||||||
|
/repos/#RepoKeyHashid/inbox RepoInboxR GET POST
|
||||||
|
/repos/#RepoKeyHashid/outbox RepoOutboxR GET
|
||||||
|
/repos/#RepoKeyHashid/outbox/#OutboxItemKeyHashid RepoOutboxItemR GET
|
||||||
|
/repos/#RepoKeyHashid/followers RepoFollowersR GET
|
||||||
|
|
||||||
|
/repos/#RepoKeyHashid/_darcs/+Texts DarcsDownloadR GET
|
||||||
|
/repos/#RepoKeyHashid/info/refs GitRefDiscoverR GET
|
||||||
|
/repos/#RepoKeyHashid/git-upload-pack GitUploadRequestR POST
|
||||||
|
|
||||||
|
/repos/#RepoKeyHashid/source/+Texts RepoSourceR GET
|
||||||
|
/repos/#RepoKeyHashid/source-by/#Text/+Texts RepoBranchSourceR GET
|
||||||
|
/repos/#RepoKeyHashid/commits RepoCommitsR GET
|
||||||
|
/repos/#RepoKeyHashid/commits-by/#Text RepoBranchCommitsR GET
|
||||||
|
/repos/#RepoKeyHashid/commits/#Text RepoCommitR GET
|
||||||
|
|
||||||
|
/new-repo RepoNewR GET POST
|
||||||
|
/repos/#RepoKeyHashid/delete RepoDeleteR POST
|
||||||
|
/repos/#RepoKeyHashid/edit RepoEditR GET POST
|
||||||
|
/repos/#RepoKeyHashid/follow RepoFollowR POST
|
||||||
|
/repos/#RepoKeyHashid/unfollow RepoUnfollowR POST
|
||||||
|
|
||||||
|
/post-receive PostReceiveR POST
|
||||||
|
|
||||||
|
---- Deck --------------------------------------------------------------------
|
||||||
|
|
||||||
|
/decks/#DeckKeyHashid DeckR GET
|
||||||
|
/decks/#DeckKeyHashid/inbox DeckInboxR GET POST
|
||||||
|
/decks/#DeckKeyHashid/outbox DeckOutboxR GET
|
||||||
|
/decks/#DeckKeyHashid/outbox/#OutboxItemKeyHashid DeckOutboxItemR GET
|
||||||
|
/decks/#DeckKeyHashid/followers DeckFollowersR GET
|
||||||
|
/decks/#DeckKeyHashid/tickets DeckTicketsR GET
|
||||||
|
|
||||||
|
/decks/#DeckKeyHashid/tree DeckTreeR GET
|
||||||
|
|
||||||
|
/new-deck DeckNewR GET POST
|
||||||
|
/decks/#DeckKeyHashid/delete DeckDeleteR POST
|
||||||
|
/decks/#DeckKeyHashid/edit DeckEditR GET POST
|
||||||
|
/decks/#DeckKeyHashid/follow DeckFollowR POST
|
||||||
|
/decks/#DeckKeyHashid/unfollow DeckUnfollowR POST
|
||||||
|
|
||||||
|
---- Ticket ------------------------------------------------------------------
|
||||||
|
|
||||||
|
/decks/#DeckKeyHashid/tickets/#TicketDeckKeyHashid TicketR GET
|
||||||
|
/decks/#DeckKeyHashid/tickets/#TicketDeckKeyHashid/discussion TicketDiscussionR GET
|
||||||
|
/decks/#DeckKeyHashid/tickets/#TicketDeckKeyHashid/events TicketEventsR GET
|
||||||
|
/decks/#DeckKeyHashid/tickets/#TicketDeckKeyHashid/followers TicketFollowersR GET
|
||||||
|
/decks/#DeckKeyHashid/tickets/#TicketDeckKeyHashid/deps TicketDepsR GET
|
||||||
|
/decks/#DeckKeyHashid/tickets/#TicketDeckKeyHashid/rdeps TicketReverseDepsR GET
|
||||||
|
|
||||||
|
-- /decks/#DeckKeyHashid/new-ticket TicketNewR GET POST
|
||||||
|
-- /decks/#DeckKeyHashid/tickets/#TicketDeckKeyHashid/edit TicketEditR GET POST
|
||||||
|
-- /decks/#DeckKeyHashid/tickets/#TicketDeckKeyHashid/delete TicketDeleteR POST
|
||||||
|
-- /decks/#DeckKeyHashid/tickets/#TicketDeckKeyHashid/accept TicketAcceptR POST
|
||||||
|
-- /decks/#DeckKeyHashid/tickets/#TicketDeckKeyHashid/close TicketCloseR POST
|
||||||
|
-- /decks/#DeckKeyHashid/tickets/#TicketDeckKeyHashid/open TicketOpenR POST
|
||||||
|
-- /decks/#DeckKeyHashid/tickets/#TicketDeckKeyHashid/claim TicketClaimR POST
|
||||||
|
-- /decks/#DeckKeyHashid/tickets/#TicketDeckKeyHashid/unclaim TicketUnclaimR POST
|
||||||
|
-- /decks/#DeckKeyHashid/tickets/#TicketDeckKeyHashid/assign TicketAssignR GET POST
|
||||||
|
-- /decks/#DeckKeyHashid/tickets/#TicketDeckKeyHashid/unassign TicketUnassignR POST
|
||||||
|
-- /decks/#DeckKeyHashid/tickets/#TicketDeckKeyHashid/follow TicketFollowR POST
|
||||||
|
-- /decks/#DeckKeyHashid/tickets/#TicketDeckKeyHashid/unfollow TicketUnfollowR POST
|
||||||
|
-- /decks/#DeckKeyHashid/tickets/#TicketDeckKeyHashid/reply TicketTopReplyR GET POST
|
||||||
|
|
||||||
|
---- Ticket Dependency -------------------------------------------------------
|
||||||
|
|
||||||
|
/decks/#DeckKeyHashid/tickets/#TicketDeckKeyHashid/deps/#TicketDepKeyHashid TicketDepR GET
|
||||||
|
|
||||||
|
-- /decks/#DeckKeyHashid/tickets/#TicketDeckKeyHashid/new-dep TicketDepNewR GET POST
|
||||||
|
-- /decks/#DeckKeyHashid/tickets/#TicketDeckKeyHashid/deps/#TicketDepKeyHashid/delete TicketDepDeleteR POST
|
||||||
|
|
||||||
|
---- Loom --------------------------------------------------------------------
|
||||||
|
|
||||||
|
/looms/#LoomKeyHashid LoomR GET
|
||||||
|
/looms/#LoomKeyHashid/inbox LoomInboxR GET POST
|
||||||
|
/looms/#LoomKeyHashid/outbox LoomOutboxR GET
|
||||||
|
/looms/#LoomKeyHashid/outbox/#OutboxItemKeyHashid LoomOutboxItemR GET
|
||||||
|
/looms/#LoomKeyHashid/followers LoomFollowersR GET
|
||||||
|
/looms/#LoomKeyHashid/cloths LoomClothsR GET
|
||||||
|
|
||||||
|
-- /new-loom LoomNewR GET POST
|
||||||
|
-- /looms/#LoomKeyHashid/delete LoomDeleteR POST
|
||||||
|
-- /looms/#LoomKeyHashid/edit LoomEditR GET POST
|
||||||
|
-- /looms/#LoomKeyHashid/follow LoomFollowR POST
|
||||||
|
-- /looms/#LoomKeyHashid/unfollow LoomUnfollowR POST
|
||||||
|
|
||||||
|
---- Cloth -------------------------------------------------------------------
|
||||||
|
|
||||||
|
/looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid ClothR GET
|
||||||
|
/looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/discussion ClothDiscussionR GET
|
||||||
|
/looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/events ClothEventsR GET
|
||||||
|
/looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/followers ClothFollowersR GET
|
||||||
|
/looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/deps ClothDepsR GET
|
||||||
|
/looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/rdeps ClothReverseDepsR GET
|
||||||
|
|
||||||
|
/looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/bundles/#BundleKeyHashid BundleR GET
|
||||||
|
/looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/bundles/#BundleKeyHashid/patches/#PatchKeyHashid PatchR GET
|
||||||
|
|
||||||
|
-- /looms/#LoomKeyHashid/new-cloth ClothNewR GET POST
|
||||||
|
-- /looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/edit ClothEditR GET POST
|
||||||
|
-- /looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/delete ClothDeleteR POST
|
||||||
|
-- /looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/accept ClothAcceptR POST
|
||||||
|
-- /looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/close ClothCloseR POST
|
||||||
|
-- /looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/open ClothOpenR POST
|
||||||
|
-- /looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/claim ClothClaimR POST
|
||||||
|
-- /looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/unclaim ClothUnclaimR POST
|
||||||
|
-- /looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/assign ClothAssignR GET POST
|
||||||
|
-- /looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/unassign ClothUnassignR POST
|
||||||
|
-- /looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/follow ClothFollowR POST
|
||||||
|
-- /looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/unfollow ClothUnfollowR POST
|
||||||
|
-- /looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/reply ClothTopReplyR GET POST
|
||||||
|
|
||||||
|
---- Cloth Dependency --------------------------------------------------------
|
||||||
|
|
||||||
|
/looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/deps/#TicketDepKeyHashid ClothDepR GET
|
||||||
|
|
||||||
|
-- /looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/new-dep ClothDepNewR GET POST
|
||||||
|
-- /looms/#LoomKeyHashid/cloths/#TicketLoomKeyHashid/deps/#TicketDepKeyHashid/delete ClothDepDeleteR POST
|
||||||
|
|
90
vervis.cabal
90
vervis.cabal
|
@ -49,6 +49,7 @@ library
|
||||||
Data.Aeson.Local
|
Data.Aeson.Local
|
||||||
Data.Attoparsec.ByteString.Local
|
Data.Attoparsec.ByteString.Local
|
||||||
Data.Binary.Local
|
Data.Binary.Local
|
||||||
|
-- Data.Bitraversable.Local
|
||||||
Data.ByteString.Char8.Local
|
Data.ByteString.Char8.Local
|
||||||
Data.ByteString.Local
|
Data.ByteString.Local
|
||||||
Data.CaseInsensitive.Local
|
Data.CaseInsensitive.Local
|
||||||
|
@ -102,6 +103,7 @@ library
|
||||||
Text.Jasmine.Local
|
Text.Jasmine.Local
|
||||||
Web.ActivityAccess
|
Web.ActivityAccess
|
||||||
Web.ActivityPub
|
Web.ActivityPub
|
||||||
|
-- Web.Capability
|
||||||
Web.Hashids.Local
|
Web.Hashids.Local
|
||||||
Web.PathPieces.Local
|
Web.PathPieces.Local
|
||||||
Yesod.ActivityPub
|
Yesod.ActivityPub
|
||||||
|
@ -118,7 +120,7 @@ library
|
||||||
|
|
||||||
Vervis.Access
|
Vervis.Access
|
||||||
Vervis.ActivityPub
|
Vervis.ActivityPub
|
||||||
Vervis.ActivityPub.Recipient
|
Vervis.Actor
|
||||||
Vervis.ActorKey
|
Vervis.ActorKey
|
||||||
Vervis.API
|
Vervis.API
|
||||||
Vervis.Application
|
Vervis.Application
|
||||||
|
@ -126,58 +128,61 @@ library
|
||||||
Vervis.BinaryBody
|
Vervis.BinaryBody
|
||||||
Vervis.Changes
|
Vervis.Changes
|
||||||
Vervis.ChangeFeed
|
Vervis.ChangeFeed
|
||||||
Vervis.Client
|
--Vervis.Class.Actor
|
||||||
|
--Vervis.Client
|
||||||
|
Vervis.Cloth
|
||||||
Vervis.Colour
|
Vervis.Colour
|
||||||
Vervis.Content
|
Vervis.Content
|
||||||
Vervis.Darcs
|
Vervis.Darcs
|
||||||
|
Vervis.Delivery
|
||||||
Vervis.Discussion
|
Vervis.Discussion
|
||||||
Vervis.Federation
|
Vervis.Federation
|
||||||
Vervis.Federation.Auth
|
Vervis.Federation.Auth
|
||||||
Vervis.Federation.Discussion
|
--Vervis.Federation.Discussion
|
||||||
Vervis.Federation.Offer
|
--Vervis.Federation.Offer
|
||||||
Vervis.Federation.Push
|
--Vervis.Federation.Push
|
||||||
Vervis.Federation.Ticket
|
--Vervis.Federation.Ticket
|
||||||
Vervis.Federation.Util
|
Vervis.Federation.Util
|
||||||
Vervis.FedURI
|
Vervis.FedURI
|
||||||
Vervis.Field.Key
|
-- Vervis.Field.Key
|
||||||
Vervis.Field.Person
|
Vervis.Field.Person
|
||||||
Vervis.Field.Project
|
--Vervis.Field.Project
|
||||||
Vervis.Field.Repo
|
--Vervis.Field.Repo
|
||||||
Vervis.Field.Role
|
--Vervis.Field.Role
|
||||||
Vervis.Field.Sharer
|
--Vervis.Field.Sharer
|
||||||
Vervis.Field.Ticket
|
--Vervis.Field.Ticket
|
||||||
Vervis.Field.Workflow
|
-- Vervis.Field.Workflow
|
||||||
Vervis.Form.Discussion
|
Vervis.Form.Discussion
|
||||||
Vervis.Form.Group
|
--Vervis.Form.Group
|
||||||
Vervis.Form.Key
|
-- Vervis.Form.Key
|
||||||
Vervis.Form.Project
|
--Vervis.Form.Project
|
||||||
Vervis.Form.Repo
|
--Vervis.Form.Repo
|
||||||
Vervis.Form.Role
|
--Vervis.Form.Role
|
||||||
Vervis.Form.Ticket
|
--Vervis.Form.Ticket
|
||||||
Vervis.Form.Workflow
|
-- Vervis.Form.Workflow
|
||||||
Vervis.Formatting
|
Vervis.Formatting
|
||||||
Vervis.Foundation
|
Vervis.Foundation
|
||||||
Vervis.Git
|
Vervis.Git
|
||||||
Vervis.GraphProxy
|
Vervis.GraphProxy
|
||||||
Vervis.Handler.Client
|
Vervis.Handler.Client
|
||||||
|
Vervis.Handler.Cloth
|
||||||
Vervis.Handler.Common
|
Vervis.Handler.Common
|
||||||
|
Vervis.Handler.Deck
|
||||||
Vervis.Handler.Discussion
|
Vervis.Handler.Discussion
|
||||||
Vervis.Handler.Git
|
-- Vervis.Handler.Git
|
||||||
Vervis.Handler.Group
|
Vervis.Handler.Group
|
||||||
Vervis.Handler.Home
|
--Vervis.Handler.Inbox
|
||||||
Vervis.Handler.Inbox
|
--Vervis.Handler.Key
|
||||||
Vervis.Handler.Key
|
Vervis.Handler.Loom
|
||||||
Vervis.Handler.Patch
|
|
||||||
Vervis.Handler.Person
|
Vervis.Handler.Person
|
||||||
Vervis.Handler.Project
|
|
||||||
Vervis.Handler.Repo
|
Vervis.Handler.Repo
|
||||||
Vervis.Handler.Repo.Darcs
|
--Vervis.Handler.Repo.Darcs
|
||||||
Vervis.Handler.Repo.Git
|
--Vervis.Handler.Repo.Git
|
||||||
Vervis.Handler.Role
|
--Vervis.Handler.Role
|
||||||
Vervis.Handler.Sharer
|
--Vervis.Handler.Sharer
|
||||||
Vervis.Handler.Ticket
|
Vervis.Handler.Ticket
|
||||||
Vervis.Handler.Wiki
|
-- Vervis.Handler.Wiki
|
||||||
Vervis.Handler.Workflow
|
-- Vervis.Handler.Workflow
|
||||||
Vervis.Hook
|
Vervis.Hook
|
||||||
Vervis.KeyFile
|
Vervis.KeyFile
|
||||||
Vervis.Migration
|
Vervis.Migration
|
||||||
|
@ -193,12 +198,13 @@ library
|
||||||
Vervis.Model.Workflow
|
Vervis.Model.Workflow
|
||||||
Vervis.Paginate
|
Vervis.Paginate
|
||||||
Vervis.Palette
|
Vervis.Palette
|
||||||
Vervis.Patch
|
|
||||||
Vervis.Path
|
Vervis.Path
|
||||||
Vervis.Query
|
Vervis.Query
|
||||||
Vervis.Readme
|
Vervis.Readme
|
||||||
|
Vervis.Recipient
|
||||||
Vervis.RemoteActorStore
|
Vervis.RemoteActorStore
|
||||||
Vervis.Role
|
--Vervis.Repo
|
||||||
|
--Vervis.Role
|
||||||
Vervis.Secure
|
Vervis.Secure
|
||||||
Vervis.Settings
|
Vervis.Settings
|
||||||
Vervis.Settings.StaticFiles
|
Vervis.Settings.StaticFiles
|
||||||
|
@ -211,13 +217,13 @@ library
|
||||||
Vervis.Time
|
Vervis.Time
|
||||||
Vervis.Widget
|
Vervis.Widget
|
||||||
Vervis.Widget.Discussion
|
Vervis.Widget.Discussion
|
||||||
Vervis.Widget.Project
|
Vervis.Widget.Person
|
||||||
|
--Vervis.Widget.Project
|
||||||
Vervis.Widget.Repo
|
Vervis.Widget.Repo
|
||||||
Vervis.Widget.Role
|
--Vervis.Widget.Role
|
||||||
Vervis.Widget.Sharer
|
--Vervis.Widget.Ticket
|
||||||
Vervis.Widget.Ticket
|
-- Vervis.Widget.Workflow
|
||||||
Vervis.Widget.Workflow
|
-- Vervis.Wiki
|
||||||
Vervis.Wiki
|
|
||||||
Vervis.WorkItem
|
Vervis.WorkItem
|
||||||
-- other-modules:
|
-- other-modules:
|
||||||
default-extensions: TemplateHaskell
|
default-extensions: TemplateHaskell
|
||||||
|
@ -244,6 +250,8 @@ library
|
||||||
-- for parsing commands sent over SSH and Darcs patch
|
-- for parsing commands sent over SSH and Darcs patch
|
||||||
-- metadata
|
-- metadata
|
||||||
, attoparsec
|
, attoparsec
|
||||||
|
-- For LocalActorBy and LocalStageBy
|
||||||
|
, barbies
|
||||||
, base
|
, base
|
||||||
-- for hex display of Darcs patch hashes
|
-- for hex display of Darcs patch hashes
|
||||||
, base16-bytestring
|
, base16-bytestring
|
||||||
|
@ -399,7 +407,7 @@ library
|
||||||
|
|
||||||
if flag(dev) || flag(library-only)
|
if flag(dev) || flag(library-only)
|
||||||
cpp-options: -DDEVELOPMENT
|
cpp-options: -DDEVELOPMENT
|
||||||
ghc-options: -Wall -fwarn-tabs -O0 -Werror=incomplete-patterns
|
ghc-options: -Wall -fwarn-tabs -O0 -Werror=incomplete-patterns -Werror=missing-fields
|
||||||
else
|
else
|
||||||
ghc-options: -Wall -fwarn-tabs -O2
|
ghc-options: -Wall -fwarn-tabs -O2
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue