fix(activitypub): Add quote policy fields to reply objects
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
- Mirror article quote policy/interactionPolicy logic for replies - Add test coverage for reply quote policy fields
This commit is contained in:
parent
2bea8c6b82
commit
8c12db13da
2 changed files with 93 additions and 0 deletions
|
|
@ -194,6 +194,28 @@ class ActivityPubRepliesMixin(ActivityPubPublishMixin):
|
||||||
|
|
||||||
activity_type = "Update" if self._is_published(url) else "Create"
|
activity_type = "Update" if self._is_published(url) else "Create"
|
||||||
|
|
||||||
|
# Build quote policy (same logic as articles)
|
||||||
|
quote_control = None
|
||||||
|
quote_policy = None
|
||||||
|
interaction_policy = None
|
||||||
|
if config.activitypub_quote_control:
|
||||||
|
quote_control = {"quotePolicy": config.activitypub_quote_control}
|
||||||
|
quote_policy = config.activitypub_quote_control
|
||||||
|
|
||||||
|
if config.activitypub_quote_control == "public":
|
||||||
|
allowed = ["https://www.w3.org/ns/activitystreams#Public"]
|
||||||
|
elif config.activitypub_quote_control == "followers":
|
||||||
|
allowed = [self.handler.followers_url]
|
||||||
|
elif config.activitypub_quote_control == "following":
|
||||||
|
allowed = [self.handler.following_url]
|
||||||
|
elif config.activitypub_quote_control == "nobody":
|
||||||
|
allowed = []
|
||||||
|
else:
|
||||||
|
allowed = []
|
||||||
|
|
||||||
|
can_quote = {"automaticApproval": allowed}
|
||||||
|
interaction_policy = {"canQuote": can_quote}
|
||||||
|
|
||||||
obj = Object(
|
obj = Object(
|
||||||
id=url,
|
id=url,
|
||||||
type="Note", # Replies are Notes, not Articles
|
type="Note", # Replies are Notes, not Articles
|
||||||
|
|
@ -208,6 +230,9 @@ class ActivityPubRepliesMixin(ActivityPubPublishMixin):
|
||||||
cc=cc_list,
|
cc=cc_list,
|
||||||
tag=mention_tags + hashtag_tags,
|
tag=mention_tags + hashtag_tags,
|
||||||
attachment=attachments,
|
attachment=attachments,
|
||||||
|
quote_control=quote_control,
|
||||||
|
quote_policy=quote_policy,
|
||||||
|
interaction_policy=interaction_policy,
|
||||||
)
|
)
|
||||||
|
|
||||||
obj.media_type = "text/html"
|
obj.media_type = "text/html"
|
||||||
|
|
|
||||||
|
|
@ -1752,6 +1752,74 @@ class ReplyMentionTest(unittest.TestCase):
|
||||||
finally:
|
finally:
|
||||||
config.state_dir = orig_state_dir
|
config.state_dir = orig_state_dir
|
||||||
|
|
||||||
|
@skip_if_no_pubby
|
||||||
|
def test_build_reply_object_includes_quote_policy(self):
|
||||||
|
"""build_reply_object should include quote policy fields like articles."""
|
||||||
|
from pubby import ActivityPubHandler
|
||||||
|
from madblog.activitypub import ActivityPubIntegration
|
||||||
|
from madblog.config import config
|
||||||
|
|
||||||
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
|
pages_dir = Path(tmpdir) / "pages"
|
||||||
|
pages_dir.mkdir()
|
||||||
|
replies_dir = Path(tmpdir) / "replies"
|
||||||
|
replies_dir.mkdir()
|
||||||
|
state_dir = Path(tmpdir) / "state"
|
||||||
|
state_dir.mkdir()
|
||||||
|
|
||||||
|
(replies_dir / "test-article").mkdir()
|
||||||
|
reply_file = replies_dir / "test-article" / "reply.md"
|
||||||
|
reply_file.write_text(
|
||||||
|
"[//]: # (reply-to: https://example.com/article/test-article)\n\n"
|
||||||
|
"Test reply content\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
handler = MagicMock(spec=ActivityPubHandler)
|
||||||
|
handler.actor_path = "/ap/actor"
|
||||||
|
handler.followers_url = "https://example.com/ap/followers"
|
||||||
|
handler.following_url = "https://example.com/ap/following"
|
||||||
|
handler.storage = MagicMock()
|
||||||
|
handler.storage.get_interaction_by_object_id.return_value = None
|
||||||
|
|
||||||
|
orig_state_dir = config.state_dir
|
||||||
|
orig_quote_control = config.activitypub_quote_control
|
||||||
|
|
||||||
|
try:
|
||||||
|
config.state_dir = str(state_dir)
|
||||||
|
config.activitypub_quote_control = "public"
|
||||||
|
|
||||||
|
integration = ActivityPubIntegration(
|
||||||
|
handler=handler,
|
||||||
|
pages_dir=str(pages_dir),
|
||||||
|
base_url="https://example.com",
|
||||||
|
replies_dir=str(replies_dir),
|
||||||
|
)
|
||||||
|
|
||||||
|
url = integration.reply_file_to_url(str(reply_file))
|
||||||
|
actor_url = "https://example.com/ap/actor"
|
||||||
|
|
||||||
|
obj, _ = integration.build_reply_object(
|
||||||
|
str(reply_file), url, actor_url, allow_network=False
|
||||||
|
)
|
||||||
|
|
||||||
|
# Quote policy fields should be present
|
||||||
|
self.assertEqual(obj.quote_control, {"quotePolicy": "public"})
|
||||||
|
self.assertEqual(obj.quote_policy, "public")
|
||||||
|
self.assertEqual(
|
||||||
|
obj.interaction_policy,
|
||||||
|
{
|
||||||
|
"canQuote": {
|
||||||
|
"automaticApproval": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
finally:
|
||||||
|
config.state_dir = orig_state_dir
|
||||||
|
config.activitypub_quote_control = orig_quote_control
|
||||||
|
|
||||||
|
|
||||||
class ReplyCollisionAvoidanceTest(unittest.TestCase):
|
class ReplyCollisionAvoidanceTest(unittest.TestCase):
|
||||||
"""Test collision avoidance for reply delete/recreate scenarios."""
|
"""Test collision avoidance for reply delete/recreate scenarios."""
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue