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(
|
models.IntegerField(
|
||||||
choices=[
|
choices=[
|
||||||
(0, "Public"),
|
(0, "Public"),
|
||||||
|
(4, "Local Only"),
|
||||||
(1, "Unlisted"),
|
(1, "Unlisted"),
|
||||||
(2, "Followers"),
|
(2, "Followers"),
|
||||||
(3, "Mentioned"),
|
(3, "Mentioned"),
|
||||||
|
|
|
@ -64,6 +64,7 @@ class Post(StatorModel):
|
||||||
|
|
||||||
class Visibilities(models.IntegerChoices):
|
class Visibilities(models.IntegerChoices):
|
||||||
public = 0
|
public = 0
|
||||||
|
local_only = 4
|
||||||
unlisted = 1
|
unlisted = 1
|
||||||
followers = 2
|
followers = 2
|
||||||
mentioned = 3
|
mentioned = 3
|
||||||
|
@ -261,6 +262,9 @@ class Post(StatorModel):
|
||||||
mentions.add(identity)
|
mentions.add(identity)
|
||||||
if reply_to:
|
if reply_to:
|
||||||
mentions.add(reply_to.author)
|
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
|
# Strip all HTML and apply linebreaks filter
|
||||||
content = linebreaks_filter(strip_html(content))
|
content = linebreaks_filter(strip_html(content))
|
||||||
# Make the Post object
|
# Make the Post object
|
||||||
|
@ -361,11 +365,12 @@ class Post(StatorModel):
|
||||||
reply_post = await self.ain_reply_to_post()
|
reply_post = await self.ain_reply_to_post()
|
||||||
if reply_post:
|
if reply_post:
|
||||||
targets.add(reply_post.author)
|
targets.add(reply_post.author)
|
||||||
# If this is a remote post, filter to only include local identities
|
# If this is a remote post or local-only, filter to only include
|
||||||
if not self.local:
|
# local identities
|
||||||
|
if not self.local or self.visibility == Post.Visibilities.local_only:
|
||||||
targets = {target for target in targets if target.local}
|
targets = {target for target in targets if target.local}
|
||||||
# If it's a local post, include the author
|
# If it's a local post, include the author
|
||||||
else:
|
if self.local:
|
||||||
targets.add(self.author)
|
targets.add(self.author)
|
||||||
return targets
|
return targets
|
||||||
|
|
||||||
|
|
|
@ -172,6 +172,7 @@ class Compose(FormView):
|
||||||
visibility = forms.ChoiceField(
|
visibility = forms.ChoiceField(
|
||||||
choices=[
|
choices=[
|
||||||
(Post.Visibilities.public, "Public"),
|
(Post.Visibilities.public, "Public"),
|
||||||
|
(Post.Visibilities.local_only, "Local Only"),
|
||||||
(Post.Visibilities.unlisted, "Unlisted"),
|
(Post.Visibilities.unlisted, "Unlisted"),
|
||||||
(Post.Visibilities.followers, "Followers & Mentioned Only"),
|
(Post.Visibilities.followers, "Followers & Mentioned Only"),
|
||||||
(Post.Visibilities.mentioned, "Mentioned Only"),
|
(Post.Visibilities.mentioned, "Mentioned Only"),
|
||||||
|
@ -207,7 +208,7 @@ class Compose(FormView):
|
||||||
] = self.request.identity.config_identity.default_post_visibility
|
] = self.request.identity.config_identity.default_post_visibility
|
||||||
if self.reply_to:
|
if self.reply_to:
|
||||||
initial["reply_to"] = self.reply_to.pk
|
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} "
|
initial["text"] = f"@{self.reply_to.author.handle} "
|
||||||
return initial
|
return initial
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
<i class="visibility fa-solid fa-lock" title="Followers Only"></i>
|
<i class="visibility fa-solid fa-lock" title="Followers Only"></i>
|
||||||
{% elif post.visibility == 3 %}
|
{% elif post.visibility == 3 %}
|
||||||
<i class="visibility fa-solid fa-at" title="Mentioned Only"></i>
|
<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 %}
|
{% endif %}
|
||||||
<a href="{{ post.url }}">
|
<a href="{{ post.url }}">
|
||||||
{% if post.published %}
|
{% 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.fixture
|
||||||
@pytest.mark.django_db
|
@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.
|
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(
|
identity = Identity.objects.create(
|
||||||
actor_uri="https://example.com/@test@example.com/",
|
actor_uri="https://example.com/@test@example.com/",
|
||||||
username="test",
|
username="test",
|
||||||
|
@ -84,9 +89,25 @@ def identity(user):
|
||||||
return identity
|
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.fixture
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def remote_identity():
|
def remote_identity() -> Identity:
|
||||||
"""
|
"""
|
||||||
Creates a basic remote test identity with a domain.
|
Creates a basic remote test identity with a domain.
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in a new issue