Fixed #616: Do followers-only properly

This commit is contained in:
Andrew Godwin 2023-07-22 10:38:22 -06:00
parent 1dd076ff7d
commit 759d5ac052
4 changed files with 77 additions and 8 deletions

View file

@ -623,6 +623,7 @@ class Post(StatorModel):
"""
Returns the AP JSON for this object
"""
self.author.ensure_uris()
value = {
"to": [],
"cc": [],
@ -655,11 +656,14 @@ class Post(StatorModel):
if self.edited:
value["updated"] = format_ld_date(self.edited)
# Targeting
# TODO: Add followers object
if self.visibility == self.Visibilities.public:
value["to"].append("as:Public")
elif self.visibility == self.Visibilities.unlisted:
value["cc"].append("as:Public")
elif (
self.visibility == self.Visibilities.followers and self.author.followers_uri
):
value["to"].append(self.author.followers_uri)
# Mentions
for mention in self.mentions.all():
value["tag"].append(mention.to_ap_tag())
@ -922,6 +926,8 @@ class Post(StatorModel):
post.visibility = Post.Visibilities.public
elif "public" in cc or "as:public" in cc:
post.visibility = Post.Visibilities.unlisted
elif post.author.followers_uri in to:
post.visibility = Post.Visibilities.followers
# Attachments
# These have no IDs, so we have to wipe them each time
post.attachments.all().delete()

View file

@ -1,5 +1,5 @@
0.9
===
0.10
====
*Released: Not Yet Released*
@ -15,6 +15,11 @@ This release's major changes:
* TBC
Minor changes also include:
* Followers-only mode now works correctly inbound and outbound (though outbound
may need the other server to refresh the profile first).
If you'd like to help with code, design, or other areas, see
:doc:`/contributing` to see how to get in touch.

View file

@ -499,3 +499,45 @@ def test_post_hashtag_to_ap(identity: Identity, config_system):
]
assert "#world" in ap["object"]["content"]
assert 'rel="tag"' in ap["object"]["content"]
@pytest.mark.django_db
@pytest.mark.parametrize(
"visibility",
[
Post.Visibilities.public,
Post.Visibilities.unlisted,
Post.Visibilities.followers,
Post.Visibilities.mentioned,
],
)
def test_post_targets_to_ap(
identity: Identity, other_identity: Identity, visibility: Post.Visibilities
):
"""
Ensures that posts have the right targets in AP form.
"""
# Make a post
post = Post.objects.create(
content="<p>Hello @other</p>",
author=identity,
local=True,
visibility=visibility,
)
post.mentions.add(other_identity)
# Check its AP targets
ap_dict = post.to_ap()
if visibility == Post.Visibilities.public:
assert ap_dict["to"] == ["as:Public"]
assert ap_dict["cc"] == [other_identity.actor_uri]
elif visibility == Post.Visibilities.unlisted:
assert "to" not in ap_dict
assert ap_dict["cc"] == ["as:Public", other_identity.actor_uri]
elif visibility == Post.Visibilities.followers:
assert ap_dict["to"] == [identity.followers_uri]
assert ap_dict["cc"] == [other_identity.actor_uri]
elif visibility == Post.Visibilities.mentioned:
assert "to" not in ap_dict
assert ap_dict["cc"] == [other_identity.actor_uri]

View file

@ -317,6 +317,19 @@ class Identity(StatorModel):
for data in self.metadata
]
def ensure_uris(self):
"""
Ensures that local identities have all the URIs populated on their fields
(this lets us add new ones easily)
"""
if self.local:
self.inbox_uri = self.actor_uri + "inbox/"
self.outbox_uri = self.actor_uri + "outbox/"
self.featured_collection_uri = self.actor_uri + "collections/featured/"
self.followers_uri = self.actor_uri + "followers/"
self.following_uri = self.actor_uri + "following/"
self.shared_inbox_uri = f"https://{self.domain.uri_domain}/inbox/"
### Alternate constructors/fetchers ###
@classmethod
@ -482,12 +495,15 @@ class Identity(StatorModel):
def to_ap(self):
from activities.models import Emoji
self.ensure_uris()
response = {
"id": self.actor_uri,
"type": self.actor_type.title(),
"inbox": self.actor_uri + "inbox/",
"outbox": self.actor_uri + "outbox/",
"featured": self.actor_uri + "collections/featured/",
"inbox": self.inbox_uri,
"outbox": self.outbox_uri,
"featured": self.featured_collection_uri,
"followers": self.followers_uri,
"following": self.following_uri,
"preferredUsername": self.username,
"publicKey": {
"id": self.public_key_id,
@ -514,9 +530,9 @@ class Identity(StatorModel):
"mediaType": media_type_from_filename(self.image.name),
"url": self.image.url,
}
if self.local:
if self.shared_inbox_uri:
response["endpoints"] = {
"sharedInbox": f"https://{self.domain.uri_domain}/inbox/",
"sharedInbox": self.shared_inbox_uri,
}
if self.metadata:
response["attachment"] = [