Local-only posting

pull/57/head
Michael Manfre 2022-11-26 12:09:31 -05:00 zatwierdzone przez GitHub
rodzic c758858392
commit 849c221aee
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
6 zmienionych plików z 144 dodań i 7 usunięć

Wyświetl plik

@ -60,6 +60,7 @@ class Migration(migrations.Migration):
models.IntegerField(
choices=[
(0, "Public"),
(4, "Local Only"),
(1, "Unlisted"),
(2, "Followers"),
(3, "Mentioned"),

Wyświetl plik

@ -64,6 +64,7 @@ class Post(StatorModel):
class Visibilities(models.IntegerChoices):
public = 0
local_only = 4
unlisted = 1
followers = 2
mentioned = 3
@ -261,6 +262,9 @@ class Post(StatorModel):
mentions.add(identity)
if reply_to:
mentions.add(reply_to.author)
# Maintain local-only for replies
if reply_to.visibility == reply_to.Visibilities.local_only:
visibility = reply_to.Visibilities.local_only
# Strip all HTML and apply linebreaks filter
content = linebreaks_filter(strip_html(content))
# Make the Post object
@ -361,11 +365,12 @@ class Post(StatorModel):
reply_post = await self.ain_reply_to_post()
if reply_post:
targets.add(reply_post.author)
# If this is a remote post, filter to only include local identities
if not self.local:
# If this is a remote post or local-only, filter to only include
# local identities
if not self.local or self.visibility == Post.Visibilities.local_only:
targets = {target for target in targets if target.local}
# If it's a local post, include the author
else:
if self.local:
targets.add(self.author)
return targets

Wyświetl plik

@ -172,6 +172,7 @@ class Compose(FormView):
visibility = forms.ChoiceField(
choices=[
(Post.Visibilities.public, "Public"),
(Post.Visibilities.local_only, "Local Only"),
(Post.Visibilities.unlisted, "Unlisted"),
(Post.Visibilities.followers, "Followers & Mentioned Only"),
(Post.Visibilities.mentioned, "Mentioned Only"),
@ -207,7 +208,7 @@ class Compose(FormView):
] = self.request.identity.config_identity.default_post_visibility
if self.reply_to:
initial["reply_to"] = self.reply_to.pk
initial["visibility"] = Post.Visibilities.unlisted
initial["visibility"] = self.reply_to.visibility
initial["text"] = f"@{self.reply_to.author.handle} "
return initial

Wyświetl plik

@ -15,6 +15,8 @@
<i class="visibility fa-solid fa-lock" title="Followers Only"></i>
{% elif post.visibility == 3 %}
<i class="visibility fa-solid fa-at" title="Mentioned Only"></i>
{% elif post.visibility == 4 %}
<i class="visibility fa-solid fa-link-slash" title="Local Only"></i>
{% endif %}
<a href="{{ post.url }}">
{% if post.published %}

Wyświetl plik

@ -0,0 +1,107 @@
import pytest
from asgiref.sync import async_to_sync
from activities.models import Post
from users.models import Follow
@pytest.mark.django_db
def test_post_targets_simple(identity, other_identity, remote_identity):
"""
Tests that a simple top level post returns the correct targets.
"""
# Test a post with no mentions targets author
post = Post.objects.create(
content="<p>Hello</p>",
author=identity,
local=True,
)
targets = async_to_sync(post.aget_targets)()
assert targets == {identity}
# Test remote reply targets original post author
Post.objects.create(
content="<p>Reply</p>",
author=remote_identity,
local=False,
in_reply_to=post.absolute_object_uri(),
)
targets = async_to_sync(post.aget_targets)()
assert targets == {identity}
# Test a post with local and remote mentions
post = Post.objects.create(
content="<p>Hello @test and @other</p>",
author=identity,
local=True,
)
# Mentions are targeted
post.mentions.add(remote_identity)
post.mentions.add(other_identity)
targets = async_to_sync(post.aget_targets)()
# Targets everyone
assert targets == {identity, other_identity, remote_identity}
# Test remote post with mentions
post.local = False
post.save()
targets = async_to_sync(post.aget_targets)()
# Only targets locals
assert targets == {identity, other_identity}
@pytest.mark.django_db
def test_post_local_only(identity, other_identity, remote_identity):
"""
Tests that a simple top level post returns the correct targets.
"""
# Test a short username (remote)
post = Post.objects.create(
content="<p>Hello @test and @other</p>",
author=identity,
local=True,
visibility=Post.Visibilities.local_only,
)
post.mentions.add(remote_identity)
post.mentions.add(other_identity)
# Remote mention is not targeted
post.mentions.add(remote_identity)
targets = async_to_sync(post.aget_targets)()
assert targets == {identity, other_identity}
@pytest.mark.django_db
def test_post_followers(identity, other_identity, remote_identity):
Follow.objects.create(source=other_identity, target=identity)
Follow.objects.create(source=remote_identity, target=identity)
# Test Public post w/o mentions targets self and followers
post = Post.objects.create(
content="<p>Hello</p>",
author=identity,
local=True,
visibility=Post.Visibilities.public,
)
targets = async_to_sync(post.aget_targets)()
assert targets == {identity, other_identity, remote_identity}
# Remote post only targets local followers
post.local = False
post.save()
targets = async_to_sync(post.aget_targets)()
assert targets == {identity, other_identity}
# Local Only post only targets local followers
post.local = True
post.visibility = Post.Visibilities.local_only
post.save()
targets = async_to_sync(post.aget_targets)()
assert targets == {identity, other_identity}
# Mentioned posts do not target unmentioned followers
post.visibility = Post.Visibilities.mentioned
post.save()
targets = async_to_sync(post.aget_targets)()
assert targets == {identity}

Wyświetl plik

@ -68,11 +68,16 @@ def user() -> User:
@pytest.fixture
@pytest.mark.django_db
def identity(user):
def domain() -> Domain:
return Domain.objects.create(domain="example.com", local=True, public=True)
@pytest.fixture
@pytest.mark.django_db
def identity(user, domain) -> Identity:
"""
Creates a basic test identity with a user and domain.
"""
domain = Domain.objects.create(domain="example.com", local=True, public=True)
identity = Identity.objects.create(
actor_uri="https://example.com/@test@example.com/",
username="test",
@ -84,9 +89,25 @@ def identity(user):
return identity
@pytest.fixture
def other_identity(user, domain) -> Identity:
"""
Creates a different basic test identity with a user and domain.
"""
identity = Identity.objects.create(
actor_uri="https://example.com/@other@example.com/",
username="other",
domain=domain,
name="Other User",
local=True,
)
identity.users.set([user])
return identity
@pytest.fixture
@pytest.mark.django_db
def remote_identity():
def remote_identity() -> Identity:
"""
Creates a basic remote test identity with a domain.
"""