mirror of
https://github.com/jointakahe/takahe.git
synced 2024-11-25 16:51:00 +00:00
Local-only posting
This commit is contained in:
parent
c758858392
commit
849c221aee
6 changed files with 144 additions and 7 deletions
|
@ -60,6 +60,7 @@ class Migration(migrations.Migration):
|
|||
models.IntegerField(
|
||||
choices=[
|
||||
(0, "Public"),
|
||||
(4, "Local Only"),
|
||||
(1, "Unlisted"),
|
||||
(2, "Followers"),
|
||||
(3, "Mentioned"),
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 %}
|
||||
|
|
107
tests/activities/models/test_post_targets.py
Normal file
107
tests/activities/models/test_post_targets.py
Normal file
|
@ -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}
|
|
@ -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.
|
||||
"""
|
||||
|
|
Loading…
Reference in a new issue