Skip to content

Commit

Permalink
as2: add new link_tags function
Browse files Browse the repository at this point in the history
  • Loading branch information
snarfed committed May 20, 2024
1 parent ed20045 commit efc84ce
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ Changelog
* `as2`:
* Add support for the `Application`, `Block`, `Flag`, and `Link` types.
* Generalize actor logic in `to/from_as1` across all actor types, not just `Person`.
* Add new `link_tags` function.
* `atom`:
* `activities_to_atom`: handle image attachments without `url` field.
* `bluesky`:
Expand Down
40 changes: 40 additions & 0 deletions granary/as2.py
Original file line number Diff line number Diff line change
Expand Up @@ -567,3 +567,43 @@ def address(actor):
match = re.match(r'^https?://(.+)/(users/|profile/|@)(.+)$', url)
if match:
return match.expand(r'@\3@\1')


def link_tags(obj):
"""Adds HTML links to ``content`` for tags with ``startIndex`` and ``length``.
``content`` is modified in place. If ``content_is_html`` is ``true``, does
nothing. Otherwise, sets it to ``true`` if at least one link is added. Tags
without ``startIndex``/``length`` are ignored.
TODO: duplicated in :func:`microformats2.render_content`. unify?
Args:
obj (dict): AS2 JSON object
"""
content = obj.get('content')
if not content or obj.get('content_is_html'):
return

# extract indexed tags, preserving order
tags = [tag for tag in obj.get('tag', [])
if 'startIndex' in tag and 'length' in tag and 'href' in tag]
tags.sort(key=lambda t: t['startIndex'])

# linkify embedded mention tags inside content.
last_end = 0
orig = content
linked = ''
for tag in tags:
start = tag['startIndex']
if start < last_end:
logger.warning(f'tag indices overlap! skipping {tag["href"]}')
continue
end = start + tag['length']
linked = f"{linked}{orig[last_end:start]}<a href=\"{tag['href']}\">{orig[start:end]}</a>"
last_end = end
obj['content_is_html'] = True
del tag['startIndex']
del tag['length']

obj['content'] = linked + orig[last_end:]
1 change: 1 addition & 0 deletions granary/microformats2.py
Original file line number Diff line number Diff line change
Expand Up @@ -998,6 +998,7 @@ def render_content(obj, include_location=True, synthesize_content=True,
tags.setdefault(as1.object_type(t), []).append(t)

# linkify embedded mention tags inside content.
# TODO: duplicated in :func:`as2.link_tags`. unify?
if mentions:
mentions.sort(key=lambda t: t['startIndex'])
last_end = 0
Expand Down
32 changes: 32 additions & 0 deletions granary/tests/test_as2.py
Original file line number Diff line number Diff line change
Expand Up @@ -472,3 +472,35 @@ def test_to_as1_flag(self):
# note that this is the user being reported
'to': 'http://bob',
}))

def test_link_tags(self):
# no indices, shouuld be a noop
obj = {
'content': 'foo\nbar\nbaz',
'tag': [
{'href': 'http://bar'},
{'href': 'http://baz'},
],
}
as2.link_tags(obj)
self.assert_equals('foo\nbar\nbaz', obj['content'])

# with indices, should link and then remove indices
obj['tag'] = [
{'href': 'http://bar', 'startIndex': 4, 'length': 3},
{'href': 'http://baz', 'startIndex': 8, 'length': 3},
]

as2.link_tags(obj)
self.assert_equals({
'content': """\
foo
<a href="http://bar">bar</a>
<a href="http://baz">baz</a>
""",
'content_is_html': True,
'tag': [
{'href': 'http://bar'},
{'href': 'http://baz'},
],
}, obj)

0 comments on commit efc84ce

Please sign in to comment.