diff --git a/CHANGELOG.md b/CHANGELOG.md index 414e6c4..85733fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ * The NodeInfo2 hostmeta parser now cleans the port out of the host name. +* URL's in outgoing text content are now linkified for the HTML representation + of the content for ActivityPub payloads. + ### Fixed * Don't crash loudly when fetching webfinger for Diaspora that does not contain XML. diff --git a/federation/entities/mixins.py b/federation/entities/mixins.py index c401198..25128da 100644 --- a/federation/entities/mixins.py +++ b/federation/entities/mixins.py @@ -8,6 +8,7 @@ from commonmark import commonmark from federation.entities.activitypub.enums import ActivityType from federation.entities.utils import get_name_for_profile +from federation.utils.text import process_text_links class BaseEntity: @@ -216,6 +217,8 @@ class RawContentMixin(BaseEntity): if not display_name: display_name = mention rendered = rendered.replace("@{%s}" % mention, f'@{display_name}') + # Finally linkify remaining URL's that are not links + rendered = process_text_links(rendered) return rendered return self.raw_content diff --git a/federation/tests/entities/activitypub/test_entities.py b/federation/tests/entities/activitypub/test_entities.py index 015d2c3..9f6f0f5 100644 --- a/federation/tests/entities/activitypub/test_entities.py +++ b/federation/tests/entities/activitypub/test_entities.py @@ -69,6 +69,40 @@ class TestEntitiesConvertToAS2: 'published': '2019-04-27T00:00:00', } + def test_comment_to_as2__url_in_raw_content(self, activitypubcomment): + activitypubcomment.raw_content = 'raw_content http://example.com' + result = activitypubcomment.to_as2() + assert result == { + '@context': [ + 'https://www.w3.org/ns/activitystreams', + {"pyfed": "https://docs.jasonrobinson.me/ns/python-federation"}, + {'Hashtag': 'as:Hashtag'}, + 'https://w3id.org/security/v1', + {'sensitive': 'as:sensitive'}, + ], + 'type': 'Create', + 'id': 'http://127.0.0.1:8000/post/123456/#create', + 'actor': 'http://127.0.0.1:8000/profile/123456/', + 'object': { + 'id': 'http://127.0.0.1:8000/post/123456/', + 'type': 'Note', + 'attributedTo': 'http://127.0.0.1:8000/profile/123456/', + 'content': '

raw_content ' + 'http://example.com

', + 'published': '2019-04-27T00:00:00', + 'inReplyTo': 'http://127.0.0.1:8000/post/012345/', + 'sensitive': False, + 'summary': None, + 'tag': [], + 'url': '', + 'source': { + 'content': 'raw_content http://example.com', + 'mediaType': 'text/markdown', + }, + }, + 'published': '2019-04-27T00:00:00', + } + def test_follow_to_as2(self, activitypubfollow): result = activitypubfollow.to_as2() assert result == { @@ -145,8 +179,8 @@ class TestEntitiesConvertToAS2: 'id': 'http://127.0.0.1:8000/post/123456/', 'type': 'Note', 'attributedTo': 'http://127.0.0.1:8000/profile/123456/', - 'content': """

raw_content

-

@{someone@localhost.local} @Bob Bobértson

""", + 'content': '

raw_content

\n

@{someone@localhost.local} @Bob Bobértson

', 'published': '2019-04-27T00:00:00', 'inReplyTo': None, 'sensitive': False,