epicyon/webapp_post.py

2309 lines
95 KiB
Python
Raw Normal View History

2020-11-09 19:42:09 +00:00
__filename__ = "webapp_post.py"
__author__ = "Bob Mottram"
__license__ = "AGPL3+"
2021-01-26 10:07:42 +00:00
__version__ = "1.2.0"
2020-11-09 19:42:09 +00:00
__maintainer__ = "Bob Mottram"
2021-09-10 16:14:50 +00:00
__email__ = "bob@libreserver.org"
2020-11-09 19:42:09 +00:00
__status__ = "Production"
2021-06-15 15:08:12 +00:00
__module_group__ = "Web Interface"
2020-11-09 19:42:09 +00:00
import os
import time
2021-11-11 22:11:12 +00:00
import urllib.parse
2020-11-09 19:42:09 +00:00
from dateutil.parser import parse
2021-12-28 21:36:27 +00:00
from auth import create_password
2021-12-29 21:55:09 +00:00
from git import is_git_patch
2020-11-09 19:42:09 +00:00
from datetime import datetime
2021-12-29 21:55:09 +00:00
from cache import get_person_from_cache
from bookmarks import bookmarked_by_person
from like import liked_by_person
from like import no_of_likes
2021-12-28 20:32:11 +00:00
from follow import is_following_actor
2021-12-29 21:55:09 +00:00
from posts import post_is_muted
from posts import get_person_box
from posts import download_announce
2021-12-28 19:33:29 +00:00
from posts import populate_replies_json
2021-12-27 17:16:57 +00:00
from utils import remove_hash_from_post_id
2021-12-27 15:43:22 +00:00
from utils import remove_html
2021-12-26 10:22:19 +00:00
from utils import get_actor_languages_list
2021-12-26 11:29:40 +00:00
from utils import get_base_content_from_post
2021-12-26 10:50:49 +00:00
from utils import get_content_from_post
2021-12-26 10:57:03 +00:00
from utils import has_object_dict
2021-12-26 23:41:34 +00:00
from utils import update_announce_collection
2021-12-26 19:15:36 +00:00
from utils import is_pgp_encrypted
2021-12-26 20:12:18 +00:00
from utils import is_dm
2021-12-26 20:20:36 +00:00
from utils import reject_post_id
2021-12-26 20:43:03 +00:00
from utils import is_recent_post
2021-12-26 14:08:58 +00:00
from utils import get_config_param
2021-12-26 12:45:03 +00:00
from utils import get_full_domain
2021-12-26 13:27:57 +00:00
from utils import is_editor
2021-12-26 20:36:08 +00:00
from utils import locate_post
2021-12-26 15:13:34 +00:00
from utils import load_json
2021-12-26 23:53:16 +00:00
from utils import get_cached_post_directory
2021-12-26 23:41:34 +00:00
from utils import get_cached_post_filename
2021-12-27 17:20:01 +00:00
from utils import get_protocol_prefixes
2021-12-28 12:20:18 +00:00
from utils import is_news_post
2021-12-28 13:49:44 +00:00
from utils import is_blog_post
2021-12-27 21:59:07 +00:00
from utils import get_display_name
2021-12-28 14:41:10 +00:00
from utils import is_public_post
2021-12-28 14:24:14 +00:00
from utils import update_recent_posts_cache
2021-12-27 11:20:57 +00:00
from utils import remove_id_ending
2021-12-27 22:19:18 +00:00
from utils import get_nickname_from_actor
2021-12-27 19:05:25 +00:00
from utils import get_domain_from_actor
2021-12-26 12:02:29 +00:00
from utils import acct_dir
2021-12-26 10:19:59 +00:00
from utils import local_actor_url
2021-12-29 21:55:09 +00:00
from content import limit_repeated_words
from content import replace_emoji_from_tags
from content import html_replace_quote_marks
from content import html_replace_email_quote
from content import remove_text_formatting
from content import remove_long_words
from content import get_mentions_from_html
from content import switch_words
from person import is_person_snoozed
from person import get_person_avatar_url
from announce import announced_by_person
from webapp_utils import get_banner_file
from webapp_utils import get_avatar_image_url
from webapp_utils import update_avatar_image_cache
from webapp_utils import load_individual_post_as_html_from_cache
from webapp_utils import add_emoji_to_display_name
from webapp_utils import post_contains_public
from webapp_utils import get_content_warning_button
from webapp_utils import get_post_attachments_as_html
from webapp_utils import html_header_with_external_style
from webapp_utils import html_footer
from webapp_utils import get_broken_link_substitute
from webapp_media import add_embedded_elements
from webapp_question import insert_question
from devices import e2e_edecrypt_message_from_device
from webfinger import webfinger_handle
from speaker import update_speaker
from languages import auto_translate_post
from blocking import is_blocked
2021-12-30 10:16:57 +00:00
from blocking import add_cw_from_lists
2021-12-29 21:55:09 +00:00
from reaction import html_emoji_reactions
def _html_post_metadata_open_graph(domain: str, post_json_object: {}) -> str:
"""Returns html OpenGraph metadata for a post
"""
metadata = \
2021-11-07 10:54:06 +00:00
" <meta content=\"" + domain + "\" property=\"og:site_name\" />\n"
metadata += \
2021-11-07 10:54:06 +00:00
" <meta content=\"article\" property=\"og:type\" />\n"
2021-12-25 22:09:19 +00:00
objJson = post_json_object
2021-12-26 10:57:03 +00:00
if has_object_dict(post_json_object):
2021-12-25 22:09:19 +00:00
objJson = post_json_object['object']
if objJson.get('attributedTo'):
if isinstance(objJson['attributedTo'], str):
attrib = objJson['attributedTo']
2021-12-27 22:19:18 +00:00
actorNick = get_nickname_from_actor(attrib)
2021-12-27 19:05:25 +00:00
actorDomain, _ = get_domain_from_actor(attrib)
actorHandle = actorNick + '@' + actorDomain
metadata += \
2021-11-07 10:54:06 +00:00
" <meta content=\"@" + actorHandle + \
"\" property=\"og:title\" />\n"
if objJson.get('url'):
metadata += \
2021-11-07 10:54:06 +00:00
" <meta content=\"" + objJson['url'] + \
"\" property=\"og:url\" />\n"
if objJson.get('published'):
metadata += \
2021-11-07 10:54:06 +00:00
" <meta content=\"" + objJson['published'] + \
"\" property=\"og:published_time\" />\n"
if not objJson.get('attachment') or objJson.get('sensitive'):
if objJson.get('content') and not objJson.get('sensitive'):
2021-12-27 15:43:22 +00:00
description = remove_html(objJson['content'])
metadata += \
2021-11-07 10:59:22 +00:00
" <meta content=\"" + description + \
"\" name=\"description\">\n"
metadata += \
2021-11-07 10:59:22 +00:00
" <meta content=\"" + description + \
"\" name=\"og:description\">\n"
return metadata
2021-11-07 11:32:08 +00:00
# metadata for attachment
for attachJson in objJson['attachment']:
if not isinstance(attachJson, dict):
continue
if not attachJson.get('mediaType'):
continue
2021-11-07 10:48:00 +00:00
if not attachJson.get('url'):
continue
if not attachJson.get('name'):
continue
description = None
if attachJson['mediaType'].startswith('image/'):
description = 'Attached: 1 image'
elif attachJson['mediaType'].startswith('video/'):
description = 'Attached: 1 video'
elif attachJson['mediaType'].startswith('audio/'):
description = 'Attached: 1 audio'
2021-11-07 10:48:00 +00:00
if description:
if objJson.get('content') and not objJson.get('sensitive'):
2021-12-27 15:43:22 +00:00
description += '\n\n' + remove_html(objJson['content'])
metadata += \
2021-11-07 10:59:22 +00:00
" <meta content=\"" + description + \
"\" name=\"description\">\n"
metadata += \
2021-11-07 10:59:22 +00:00
" <meta content=\"" + description + \
"\" name=\"og:description\">\n"
metadata += \
2021-11-07 10:54:06 +00:00
" <meta content=\"" + attachJson['url'] + \
"\" property=\"og:image\" />\n"
metadata += \
2021-11-07 10:54:06 +00:00
" <meta content=\"" + attachJson['mediaType'] + \
"\" property=\"og:image:type\" />\n"
if attachJson.get('width'):
metadata += \
2021-11-07 11:32:08 +00:00
" <meta content=\"" + str(attachJson['width']) + \
"\" property=\"og:image:width\" />\n"
if attachJson.get('height'):
metadata += \
2021-11-07 11:32:08 +00:00
" <meta content=\"" + str(attachJson['height']) + \
"\" property=\"og:image:height\" />\n"
metadata += \
2021-11-07 10:54:06 +00:00
" <meta content=\"" + attachJson['name'] + \
"\" property=\"og:image:alt\" />\n"
if attachJson['mediaType'].startswith('image/'):
metadata += \
2021-11-07 10:54:06 +00:00
" <meta content=\"summary_large_image\" " + \
"property=\"twitter:card\" />\n"
return metadata
2021-12-29 21:55:09 +00:00
def _log_post_timing(enableTimingLog: bool, postStartTime,
debugId: str) -> None:
2020-12-01 17:23:34 +00:00
"""Create a log of timings for performance tuning
"""
if not enableTimingLog:
return
timeDiff = int((time.time() - postStartTime) * 1000)
if timeDiff > 100:
print('TIMING INDIV ' + debugId + ' = ' + str(timeDiff))
2021-12-29 21:55:09 +00:00
def prepare_html_post_nickname(nickname: str, postHtml: str) -> str:
2021-02-02 21:54:29 +00:00
"""html posts stored in memory are for all accounts on the instance
and they're indexed by id. However, some incoming posts may be
destined for multiple accounts (followers). This creates a problem
where the icon links whose urls begin with href="/users/nickname?
need to be changed for different nicknames to display correctly
within their timelines.
This function changes the nicknames for the icon links.
2021-02-02 21:08:33 +00:00
"""
# replace the nickname
usersStr = ' href="/users/'
if usersStr not in postHtml:
return postHtml
userFound = True
postStr = postHtml
newPostStr = ''
while userFound:
if usersStr not in postStr:
newPostStr += postStr
break
# the next part, after href="/users/nickname?
nextStr = postStr.split(usersStr, 1)[1]
if '?' in nextStr:
nextStr = nextStr.split('?', 1)[1]
else:
newPostStr += postStr
break
# append the previous text to the result
newPostStr += postStr.split(usersStr)[0]
newPostStr += usersStr + nickname + '?'
# post is now the next part
postStr = nextStr
return newPostStr
2021-12-29 21:55:09 +00:00
def prepare_post_from_html_cache(nickname: str, postHtml: str, boxName: str,
pageNumber: int) -> str:
2020-11-09 19:42:09 +00:00
"""Sets the page number on a cached html post
"""
# if on the bookmarks timeline then remain there
if boxName == 'tlbookmarks' or boxName == 'bookmarks':
postHtml = postHtml.replace('?tl=inbox', '?tl=tlbookmarks')
if '?page=' in postHtml:
pageNumberStr = postHtml.split('?page=')[1]
if '?' in pageNumberStr:
pageNumberStr = pageNumberStr.split('?')[0]
postHtml = postHtml.replace('?page=' + pageNumberStr, '?page=-999')
withPageNumber = postHtml.replace(';-999;', ';' + str(pageNumber) + ';')
withPageNumber = withPageNumber.replace('?page=-999',
'?page=' + str(pageNumber))
2021-12-29 21:55:09 +00:00
return prepare_html_post_nickname(nickname, withPageNumber)
2020-11-09 19:42:09 +00:00
2021-12-29 21:55:09 +00:00
def _save_individual_post_as_html_to_cache(base_dir: str,
nickname: str, domain: str,
post_json_object: {},
postHtml: str) -> bool:
2020-11-09 19:42:09 +00:00
"""Saves the given html for a post to a cache file
This is so that it can be quickly reloaded on subsequent
refresh of the timeline
"""
htmlPostCacheDir = \
2021-12-26 23:53:16 +00:00
get_cached_post_directory(base_dir, nickname, domain)
2020-11-09 19:42:09 +00:00
cachedPostFilename = \
2021-12-26 23:41:34 +00:00
get_cached_post_filename(base_dir, nickname, domain, post_json_object)
2020-11-09 19:42:09 +00:00
# create the cache directory if needed
if not os.path.isdir(htmlPostCacheDir):
os.mkdir(htmlPostCacheDir)
try:
with open(cachedPostFilename, 'w+') as fp:
fp.write(postHtml)
return True
2021-12-25 15:28:52 +00:00
except Exception as ex:
print('ERROR: saving post to cache, ' + str(ex))
return False
2020-11-09 19:42:09 +00:00
2021-12-29 21:55:09 +00:00
def _get_post_from_recent_cache(session,
base_dir: str,
http_prefix: str,
nickname: str, domain: str,
post_json_object: {},
postActor: str,
person_cache: {},
allowDownloads: bool,
showPublicOnly: bool,
storeToCache: bool,
boxName: str,
avatarUrl: str,
enableTimingLog: bool,
postStartTime,
pageNumber: int,
recent_posts_cache: {},
max_recent_posts: int,
signing_priv_key_pem: str) -> str:
2020-11-30 12:47:52 +00:00
"""Attempts to get the html post from the recent posts cache in memory
"""
if boxName == 'tlmedia':
return None
if showPublicOnly:
return None
tryCache = False
bmTimeline = boxName == 'bookmarks' or boxName == 'tlbookmarks'
if storeToCache or bmTimeline:
tryCache = True
if not tryCache:
return None
# update avatar if needed
if not avatarUrl:
avatarUrl = \
2021-12-29 21:55:09 +00:00
get_person_avatar_url(base_dir, postActor, person_cache,
allowDownloads)
2020-11-30 12:47:52 +00:00
2021-12-29 21:55:09 +00:00
_log_post_timing(enableTimingLog, postStartTime, '2.1')
2020-11-30 12:47:52 +00:00
2021-12-29 21:55:09 +00:00
update_avatar_image_cache(signing_priv_key_pem,
session, base_dir, http_prefix,
postActor, avatarUrl, person_cache,
allowDownloads)
2020-11-30 12:47:52 +00:00
2021-12-29 21:55:09 +00:00
_log_post_timing(enableTimingLog, postStartTime, '2.2')
2020-11-30 12:47:52 +00:00
postHtml = \
2021-12-29 21:55:09 +00:00
load_individual_post_as_html_from_cache(base_dir, nickname, domain,
post_json_object)
2020-11-30 12:47:52 +00:00
if not postHtml:
return None
2021-02-02 21:08:33 +00:00
postHtml = \
2021-12-29 21:55:09 +00:00
prepare_post_from_html_cache(nickname, postHtml, boxName, pageNumber)
2021-12-28 14:24:14 +00:00
update_recent_posts_cache(recent_posts_cache, max_recent_posts,
post_json_object, postHtml)
2021-12-29 21:55:09 +00:00
_log_post_timing(enableTimingLog, postStartTime, '3')
2020-11-30 12:47:52 +00:00
return postHtml
2021-12-29 21:55:09 +00:00
def _get_avatar_image_html(showAvatarOptions: bool,
nickname: str, domain_full: str,
avatarUrl: str, postActor: str,
translate: {}, avatarPosition: str,
pageNumber: int, messageIdStr: str) -> str:
2020-11-30 15:20:10 +00:00
"""Get html for the avatar image
"""
avatarLink = ''
if '/users/news/' not in avatarUrl:
avatarLink = ' <a class="imageAnchor" href="' + postActor + '">'
showProfileStr = 'Show profile'
if translate.get(showProfileStr):
showProfileStr = translate[showProfileStr]
2020-11-30 15:20:10 +00:00
avatarLink += \
2020-12-12 11:25:50 +00:00
'<img loading="lazy" src="' + avatarUrl + '" title="' + \
showProfileStr + '" alt=" "' + avatarPosition + \
2021-12-29 21:55:09 +00:00
get_broken_link_substitute() + '/></a>\n'
2020-11-30 15:20:10 +00:00
if showAvatarOptions and \
2021-12-26 10:00:46 +00:00
domain_full + '/users/' + nickname not in postActor:
showOptionsForThisPersonStr = 'Show options for this person'
if translate.get(showOptionsForThisPersonStr):
showOptionsForThisPersonStr = \
translate[showOptionsForThisPersonStr]
2020-11-30 15:20:10 +00:00
if '/users/news/' not in avatarUrl:
avatarLink = \
' <a class="imageAnchor" href="/users/' + \
nickname + '?options=' + postActor + \
';' + str(pageNumber) + ';' + avatarUrl + messageIdStr + '">\n'
avatarLink += \
' <img loading="lazy" title="' + \
showOptionsForThisPersonStr + '" ' + \
2021-02-07 21:10:21 +00:00
'alt="👤 ' + \
showOptionsForThisPersonStr + '" ' + \
2021-02-07 21:10:21 +00:00
'src="' + avatarUrl + '" ' + avatarPosition + \
2021-12-29 21:55:09 +00:00
get_broken_link_substitute() + '/></a>\n'
2020-11-30 15:20:10 +00:00
else:
# don't link to the person options for the news account
avatarLink += \
' <img loading="lazy" title="' + \
showOptionsForThisPersonStr + '" ' + \
2021-02-07 21:10:21 +00:00
'alt="👤 ' + \
showOptionsForThisPersonStr + '" ' + \
2021-02-07 21:10:21 +00:00
'src="' + avatarUrl + '" ' + avatarPosition + \
2021-12-29 21:55:09 +00:00
get_broken_link_substitute() + '/>\n'
2020-11-30 15:20:10 +00:00
return avatarLink.strip()
2021-12-29 21:55:09 +00:00
def _get_reply_icon_html(base_dir: str, nickname: str, domain: str,
isPublicRepeat: bool,
showIcons: bool, commentsEnabled: bool,
post_json_object: {}, pageNumberParam: str,
translate: {}, system_language: str,
conversationId: str) -> str:
2020-11-30 16:00:36 +00:00
"""Returns html for the reply icon/button
"""
replyStr = ''
2020-12-01 09:51:55 +00:00
if not (showIcons and commentsEnabled):
return replyStr
# reply is permitted - create reply icon
2021-12-27 17:16:57 +00:00
replyToLink = remove_hash_from_post_id(post_json_object['object']['id'])
2021-12-27 11:20:57 +00:00
replyToLink = remove_id_ending(replyToLink)
2021-09-16 16:47:52 +00:00
# see Mike MacGirvin's replyTo suggestion
2021-12-25 22:09:19 +00:00
if post_json_object['object'].get('replyTo'):
# check that the alternative replyTo url is not blocked
blockNickname = \
2021-12-27 22:19:18 +00:00
get_nickname_from_actor(post_json_object['object']['replyTo'])
blockDomain, _ = \
2021-12-27 19:05:25 +00:00
get_domain_from_actor(post_json_object['object']['replyTo'])
2021-12-29 21:55:09 +00:00
if not is_blocked(base_dir, nickname, domain,
blockNickname, blockDomain, {}):
2021-12-25 22:09:19 +00:00
replyToLink = post_json_object['object']['replyTo']
2021-09-16 16:47:52 +00:00
2021-12-25 22:09:19 +00:00
if post_json_object['object'].get('attributedTo'):
if isinstance(post_json_object['object']['attributedTo'], str):
2020-12-01 09:51:55 +00:00
replyToLink += \
2021-12-25 22:09:19 +00:00
'?mention=' + post_json_object['object']['attributedTo']
2021-12-26 11:29:40 +00:00
content = get_base_content_from_post(post_json_object, system_language)
if content:
2021-12-30 20:24:05 +00:00
mentionedActors = \
get_mentions_from_html(content,
"<span class=\"h-card\"><a href=\"")
2020-12-01 09:51:55 +00:00
if mentionedActors:
for actorUrl in mentionedActors:
2021-09-21 13:03:08 +00:00
if '?mention=' + actorUrl not in replyToLink:
replyToLink += '?mention=' + actorUrl
2020-12-01 09:51:55 +00:00
if len(replyToLink) > 500:
break
replyToLink += pageNumberParam
replyStr = ''
replyToThisPostStr = 'Reply to this post'
if translate.get(replyToThisPostStr):
replyToThisPostStr = translate[replyToThisPostStr]
2021-08-08 16:52:32 +00:00
conversationStr = ''
if conversationId:
2021-09-21 12:59:21 +00:00
conversationStr = '?conversationId=' + conversationId
2020-12-01 09:51:55 +00:00
if isPublicRepeat:
replyStr += \
' <a class="imageAnchor" href="/users/' + \
nickname + '?replyto=' + replyToLink + \
2021-12-25 22:09:19 +00:00
'?actor=' + post_json_object['actor'] + \
2021-08-08 16:52:32 +00:00
conversationStr + \
2020-12-01 10:03:21 +00:00
'" title="' + replyToThisPostStr + '">\n'
2020-12-01 09:51:55 +00:00
else:
2021-12-26 20:12:18 +00:00
if is_dm(post_json_object):
2020-11-30 16:00:36 +00:00
replyStr += \
2020-12-01 09:51:55 +00:00
' ' + \
'<a class="imageAnchor" href="/users/' + nickname + \
'?replydm=' + replyToLink + \
2021-12-25 22:09:19 +00:00
'?actor=' + post_json_object['actor'] + \
2021-08-08 16:52:32 +00:00
conversationStr + \
2020-12-01 10:03:21 +00:00
'" title="' + replyToThisPostStr + '">\n'
2020-11-30 16:00:36 +00:00
else:
2020-12-01 09:51:55 +00:00
replyStr += \
' ' + \
'<a class="imageAnchor" href="/users/' + nickname + \
'?replyfollowers=' + replyToLink + \
2021-12-25 22:09:19 +00:00
'?actor=' + post_json_object['actor'] + \
2021-08-08 16:52:32 +00:00
conversationStr + \
2020-12-01 10:03:21 +00:00
'" title="' + replyToThisPostStr + '">\n'
2020-11-30 16:00:36 +00:00
2020-12-01 09:51:55 +00:00
replyStr += \
' ' + \
'<img loading="lazy" title="' + \
2020-12-01 10:03:21 +00:00
replyToThisPostStr + '" alt="' + replyToThisPostStr + \
2020-12-09 13:08:26 +00:00
' |" src="/icons/reply.png"/></a>\n'
2020-11-30 16:00:36 +00:00
return replyStr
2021-12-29 21:55:09 +00:00
def _get_edit_icon_html(base_dir: str, nickname: str, domain_full: str,
post_json_object: {}, actorNickname: str,
translate: {}, isEvent: bool) -> str:
2020-11-30 16:48:30 +00:00
"""Returns html for the edit icon/button
"""
editStr = ''
2021-12-25 22:09:19 +00:00
actor = post_json_object['actor']
2021-02-11 12:27:12 +00:00
# This should either be a post which you created,
# or it could be generated from the newswire (see
2021-12-29 21:55:09 +00:00
# _add_blogs_to_newswire) in which case anyone with
2021-02-11 12:27:12 +00:00
# editor status should be able to alter it
2021-12-26 10:00:46 +00:00
if (actor.endswith('/' + domain_full + '/users/' + nickname) or
2021-12-26 13:27:57 +00:00
(is_editor(base_dir, nickname) and
2021-12-26 10:00:46 +00:00
actor.endswith('/' + domain_full + '/users/news'))):
2020-12-01 09:58:21 +00:00
2021-12-27 11:20:57 +00:00
post_id = remove_id_ending(post_json_object['object']['id'])
2020-12-01 09:58:21 +00:00
2021-12-26 19:47:06 +00:00
if '/statuses/' not in post_id:
2020-12-01 10:12:25 +00:00
return editStr
2021-12-28 13:49:44 +00:00
if is_blog_post(post_json_object):
editBlogPostStr = 'Edit blog post'
if translate.get(editBlogPostStr):
editBlogPostStr = translate[editBlogPostStr]
2021-12-28 12:20:18 +00:00
if not is_news_post(post_json_object):
2020-11-30 16:48:30 +00:00
editStr += \
' ' + \
2020-12-01 10:12:25 +00:00
'<a class="imageAnchor" href="/users/' + \
nickname + \
'/tlblogs?editblogpost=' + \
2021-12-26 19:47:06 +00:00
post_id.split('/statuses/')[1] + \
2021-09-21 12:25:32 +00:00
';actor=' + actorNickname + \
2020-12-01 10:12:25 +00:00
'" title="' + editBlogPostStr + '">' + \
2020-11-30 16:48:30 +00:00
'<img loading="lazy" title="' + \
2020-12-01 10:12:25 +00:00
editBlogPostStr + '" alt="' + editBlogPostStr + \
2020-12-09 13:08:26 +00:00
' |" src="/icons/edit.png"/></a>\n'
2020-12-01 10:12:25 +00:00
else:
editStr += \
' ' + \
'<a class="imageAnchor" href="/users/' + \
nickname + '/editnewspost=' + \
2021-12-26 19:47:06 +00:00
post_id.split('/statuses/')[1] + \
2020-12-01 10:12:25 +00:00
'?actor=' + actorNickname + \
'" title="' + editBlogPostStr + '">' + \
'<img loading="lazy" title="' + \
editBlogPostStr + '" alt="' + editBlogPostStr + \
2020-12-09 13:08:26 +00:00
' |" src="/icons/edit.png"/></a>\n'
2020-12-01 10:12:25 +00:00
elif isEvent:
editEventStr = 'Edit event'
if translate.get(editEventStr):
editEventStr = translate[editEventStr]
2020-12-01 10:12:25 +00:00
editStr += \
' ' + \
'<a class="imageAnchor" href="/users/' + nickname + \
'/tlblogs?editeventpost=' + \
2021-12-26 19:47:06 +00:00
post_id.split('/statuses/')[1] + \
2020-12-01 10:12:25 +00:00
'?actor=' + actorNickname + \
'" title="' + editEventStr + '">' + \
'<img loading="lazy" title="' + \
editEventStr + '" alt="' + editEventStr + \
2020-12-09 13:08:26 +00:00
' |" src="/icons/edit.png"/></a>\n'
2020-11-30 16:48:30 +00:00
return editStr
2021-12-29 21:55:09 +00:00
def _get_announce_icon_html(isAnnounced: bool,
postActor: str,
nickname: str, domain_full: str,
announceJsonObject: {},
post_json_object: {},
isPublicRepeat: bool,
isModerationPost: bool,
showRepeatIcon: bool,
translate: {},
pageNumberParam: str,
timelinePostBookmark: str,
boxName: str) -> str:
"""Returns html for announce icon/button
"""
announceStr = ''
2021-05-07 12:42:01 +00:00
if not showRepeatIcon:
return announceStr
if isModerationPost:
return announceStr
# don't allow announce/repeat of your own posts
announceIcon = 'repeat_inactive.png'
announceLink = 'repeat'
announceEmoji = ''
if not isPublicRepeat:
announceLink = 'repeatprivate'
repeatThisPostStr = 'Repeat this post'
if translate.get(repeatThisPostStr):
repeatThisPostStr = translate[repeatThisPostStr]
announceTitle = repeatThisPostStr
unannounceLinkStr = ''
2021-05-07 12:42:01 +00:00
2021-12-29 21:55:09 +00:00
if announced_by_person(isAnnounced,
postActor, nickname, domain_full):
2021-05-07 12:42:01 +00:00
announceIcon = 'repeat.png'
announceEmoji = '🔁 '
announceLink = 'unrepeat'
if not isPublicRepeat:
2021-05-07 12:42:01 +00:00
announceLink = 'unrepeatprivate'
undoTheRepeatStr = 'Undo the repeat'
if translate.get(undoTheRepeatStr):
undoTheRepeatStr = translate[undoTheRepeatStr]
announceTitle = undoTheRepeatStr
if announceJsonObject:
unannounceLinkStr = '?unannounce=' + \
2021-12-27 11:20:57 +00:00
remove_id_ending(announceJsonObject['id'])
2021-05-07 12:42:01 +00:00
2021-12-27 17:16:57 +00:00
announcePostId = remove_hash_from_post_id(post_json_object['object']['id'])
2021-12-27 11:20:57 +00:00
announcePostId = remove_id_ending(announcePostId)
announceLinkStr = '?' + \
2021-09-28 10:28:42 +00:00
announceLink + '=' + announcePostId + pageNumberParam
2021-05-07 12:42:01 +00:00
announceStr = \
' <a class="imageAnchor" href="/users/' + \
nickname + announceLinkStr + unannounceLinkStr + \
2021-12-25 22:09:19 +00:00
'?actor=' + post_json_object['actor'] + \
2021-05-07 12:42:01 +00:00
'?bm=' + timelinePostBookmark + \
'?tl=' + boxName + '" title="' + announceTitle + '">\n'
announceStr += \
' ' + \
2021-05-07 22:07:26 +00:00
'<img loading="lazy" title="' + announceTitle + \
'" alt="' + announceEmoji + announceTitle + \
2021-05-07 12:42:01 +00:00
' |" src="/icons/' + announceIcon + '"/></a>\n'
return announceStr
2021-12-29 21:55:09 +00:00
def _get_like_icon_html(nickname: str, domain_full: str,
isModerationPost: bool,
showLikeButton: bool,
post_json_object: {},
enableTimingLog: bool,
postStartTime,
translate: {}, pageNumberParam: str,
timelinePostBookmark: str,
boxName: str,
max_like_count: int) -> str:
2020-11-30 17:06:55 +00:00
"""Returns html for like icon/button
"""
2021-10-15 09:02:18 +00:00
if not showLikeButton or isModerationPost:
return ''
2020-11-30 17:06:55 +00:00
likeStr = ''
2021-10-15 09:02:18 +00:00
likeIcon = 'like_inactive.png'
likeLink = 'like'
likeTitle = 'Like this post'
if translate.get(likeTitle):
likeTitle = translate[likeTitle]
likeEmoji = ''
2021-12-29 21:55:09 +00:00
likeCount = no_of_likes(post_json_object)
2021-10-15 09:02:18 +00:00
2021-12-29 21:55:09 +00:00
_log_post_timing(enableTimingLog, postStartTime, '12.1')
2021-10-15 09:02:18 +00:00
likeCountStr = ''
if likeCount > 0:
2021-12-25 18:23:12 +00:00
if likeCount <= max_like_count:
2021-10-15 09:02:18 +00:00
likeCountStr = ' (' + str(likeCount) + ')'
else:
2021-12-25 18:23:12 +00:00
likeCountStr = ' (' + str(max_like_count) + '+)'
2021-12-29 21:55:09 +00:00
if liked_by_person(post_json_object, nickname, domain_full):
2021-10-15 09:02:18 +00:00
if likeCount == 1:
# liked by the reader only
likeCountStr = ''
likeIcon = 'like.png'
likeLink = 'unlike'
likeTitle = 'Undo the like'
if translate.get(likeTitle):
likeTitle = translate[likeTitle]
likeEmoji = '👍 '
2021-12-29 21:55:09 +00:00
_log_post_timing(enableTimingLog, postStartTime, '12.2')
2021-10-15 09:02:18 +00:00
likeStr = ''
if likeCountStr:
# show the number of likes next to icon
likeStr += '<label class="likesCount">'
likeStr += likeCountStr.replace('(', '').replace(')', '').strip()
likeStr += '</label>\n'
2021-12-29 21:55:09 +00:00
like_postId = remove_hash_from_post_id(post_json_object['id'])
like_postId = remove_id_ending(like_postId)
2021-10-15 09:02:18 +00:00
likeStr += \
' <a class="imageAnchor" href="/users/' + nickname + '?' + \
2021-12-29 21:55:09 +00:00
likeLink + '=' + like_postId + \
2021-10-15 09:02:18 +00:00
pageNumberParam + \
2021-12-25 22:09:19 +00:00
'?actor=' + post_json_object['actor'] + \
2021-10-15 09:02:18 +00:00
'?bm=' + timelinePostBookmark + \
'?tl=' + boxName + '" title="' + \
likeTitle + likeCountStr + '">\n'
likeStr += \
' ' + \
'<img loading="lazy" title="' + likeTitle + likeCountStr + \
'" alt="' + likeEmoji + likeTitle + \
' |" src="/icons/' + likeIcon + '"/></a>\n'
2020-11-30 17:06:55 +00:00
return likeStr
2021-12-29 21:55:09 +00:00
def _get_bookmark_icon_html(nickname: str, domain_full: str,
post_json_object: {},
isModerationPost: bool,
translate: {},
enableTimingLog: bool,
postStartTime, boxName: str,
pageNumberParam: str,
timelinePostBookmark: str) -> str:
"""Returns html for bookmark icon/button
"""
bookmarkStr = ''
2020-12-01 10:15:26 +00:00
if isModerationPost:
return bookmarkStr
bookmarkIcon = 'bookmark_inactive.png'
bookmarkLink = 'bookmark'
2021-02-07 19:11:30 +00:00
bookmarkEmoji = ''
bookmarkTitle = 'Bookmark this post'
if translate.get(bookmarkTitle):
bookmarkTitle = translate[bookmarkTitle]
2021-12-29 21:55:09 +00:00
if bookmarked_by_person(post_json_object, nickname, domain_full):
2020-12-01 10:15:26 +00:00
bookmarkIcon = 'bookmark.png'
bookmarkLink = 'unbookmark'
2021-02-07 19:11:30 +00:00
bookmarkEmoji = '🔖 '
bookmarkTitle = 'Undo the bookmark'
if translate.get(bookmarkTitle):
bookmarkTitle = translate[bookmarkTitle]
2021-12-29 21:55:09 +00:00
_log_post_timing(enableTimingLog, postStartTime, '12.6')
2021-12-27 17:16:57 +00:00
bookmarkPostId = remove_hash_from_post_id(post_json_object['object']['id'])
2021-12-27 11:20:57 +00:00
bookmarkPostId = remove_id_ending(bookmarkPostId)
2020-12-01 10:15:26 +00:00
bookmarkStr = \
' <a class="imageAnchor" href="/users/' + nickname + '?' + \
2021-09-28 10:28:42 +00:00
bookmarkLink + '=' + bookmarkPostId + \
2020-12-01 10:15:26 +00:00
pageNumberParam + \
2021-12-25 22:09:19 +00:00
'?actor=' + post_json_object['actor'] + \
2020-12-01 10:15:26 +00:00
'?bm=' + timelinePostBookmark + \
'?tl=' + boxName + '" title="' + bookmarkTitle + '">\n'
bookmarkStr += \
' ' + \
'<img loading="lazy" title="' + bookmarkTitle + '" alt="' + \
2021-02-07 19:11:30 +00:00
bookmarkEmoji + bookmarkTitle + ' |" src="/icons' + \
2020-12-01 10:15:26 +00:00
'/' + bookmarkIcon + '"/></a>\n'
return bookmarkStr
2021-12-29 21:55:09 +00:00
def _get_reaction_icon_html(nickname: str, domain_full: str,
post_json_object: {},
isModerationPost: bool,
showReactionButton: bool,
translate: {},
enableTimingLog: bool,
postStartTime, boxName: str,
pageNumberParam: str,
timelinePostReaction: str) -> str:
2021-11-11 15:02:03 +00:00
"""Returns html for reaction icon/button
"""
reactionStr = ''
2021-11-17 14:25:24 +00:00
if not showReactionButton or isModerationPost:
2021-11-11 15:02:03 +00:00
return reactionStr
reactionIcon = 'reaction.png'
reactionTitle = 'Select reaction'
if translate.get(reactionTitle):
reactionTitle = translate[reactionTitle]
2021-12-29 21:55:09 +00:00
_log_post_timing(enableTimingLog, postStartTime, '12.65')
reaction_postId = \
remove_hash_from_post_id(post_json_object['object']['id'])
reaction_postId = remove_id_ending(reaction_postId)
2021-11-11 15:02:03 +00:00
reactionStr = \
' <a class="imageAnchor" href="/users/' + nickname + \
2021-12-29 21:55:09 +00:00
'?selreact=' + reaction_postId + pageNumberParam + \
2021-12-25 22:09:19 +00:00
'?actor=' + post_json_object['actor'] + \
2021-11-11 15:02:03 +00:00
'?bm=' + timelinePostReaction + \
'?tl=' + boxName + '" title="' + reactionTitle + '">\n'
reactionStr += \
' ' + \
'<img loading="lazy" title="' + reactionTitle + '" alt="' + \
2021-11-11 15:12:14 +00:00
reactionTitle + ' |" src="/icons' + \
2021-11-11 15:02:03 +00:00
'/' + reactionIcon + '"/></a>\n'
return reactionStr
2021-12-29 21:55:09 +00:00
def _get_mute_icon_html(is_muted: bool,
postActor: str,
messageId: str,
nickname: str, domain_full: str,
allow_deletion: bool,
pageNumberParam: str,
boxName: str,
timelinePostBookmark: str,
translate: {}) -> str:
2020-11-30 17:48:35 +00:00
"""Returns html for mute icon/button
"""
muteStr = ''
2021-12-25 21:29:53 +00:00
if (allow_deletion or
2021-12-26 10:00:46 +00:00
('/' + domain_full + '/' in postActor and
2020-11-30 17:48:35 +00:00
messageId.startswith(postActor))):
return muteStr
2021-12-29 21:55:09 +00:00
if not is_muted:
muteThisPostStr = 'Mute this post'
if translate.get('Mute this post'):
muteThisPostStr = translate[muteThisPostStr]
2020-11-30 17:48:35 +00:00
muteStr = \
' <a class="imageAnchor" href="/users/' + nickname + \
'?mute=' + messageId + pageNumberParam + '?tl=' + boxName + \
'?bm=' + timelinePostBookmark + \
'" title="' + muteThisPostStr + '">\n'
2020-11-30 17:48:35 +00:00
muteStr += \
' ' + \
'<img loading="lazy" alt="' + \
muteThisPostStr + \
' |" title="' + muteThisPostStr + \
2020-12-09 13:08:26 +00:00
'" src="/icons/mute.png"/></a>\n'
2020-11-30 17:48:35 +00:00
else:
undoMuteStr = 'Undo mute'
if translate.get(undoMuteStr):
undoMuteStr = translate[undoMuteStr]
2020-11-30 17:48:35 +00:00
muteStr = \
' <a class="imageAnchor" href="/users/' + \
nickname + '?unmute=' + messageId + \
pageNumberParam + '?tl=' + boxName + '?bm=' + \
timelinePostBookmark + '" title="' + undoMuteStr + '">\n'
2020-11-30 17:48:35 +00:00
muteStr += \
' ' + \
'<img loading="lazy" alt="🔇 ' + undoMuteStr + \
' |" title="' + undoMuteStr + \
2020-12-09 13:08:26 +00:00
'" src="/icons/unmute.png"/></a>\n'
2020-11-30 17:48:35 +00:00
return muteStr
2021-12-29 21:55:09 +00:00
def _get_delete_icon_html(nickname: str, domain_full: str,
allow_deletion: bool,
postActor: str,
messageId: str,
post_json_object: {},
pageNumberParam: str,
translate: {}) -> str:
"""Returns html for delete icon/button
"""
deleteStr = ''
2021-12-25 21:29:53 +00:00
if (allow_deletion or
2021-12-26 10:00:46 +00:00
('/' + domain_full + '/' in postActor and
messageId.startswith(postActor))):
if '/users/' + nickname + '/' in messageId:
2021-12-28 12:20:18 +00:00
if not is_news_post(post_json_object):
deleteThisPostStr = 'Delete this post'
if translate.get(deleteThisPostStr):
deleteThisPostStr = translate[deleteThisPostStr]
deleteStr = \
' <a class="imageAnchor" href="/users/' + \
nickname + \
'?delete=' + messageId + pageNumberParam + \
'" title="' + deleteThisPostStr + '">\n'
deleteStr += \
' ' + \
'<img loading="lazy" alt="' + \
deleteThisPostStr + \
' |" title="' + deleteThisPostStr + \
2020-12-09 13:08:26 +00:00
'" src="/icons/delete.png"/></a>\n'
return deleteStr
2021-12-29 21:55:09 +00:00
def _get_published_date_str(post_json_object: {},
show_published_date_only: bool) -> str:
"""Return the html for the published date on a post
"""
publishedStr = ''
2020-12-01 10:19:53 +00:00
2021-12-25 22:09:19 +00:00
if not post_json_object['object'].get('published'):
2020-12-01 10:19:53 +00:00
return publishedStr
2021-12-25 22:09:19 +00:00
publishedStr = post_json_object['object']['published']
2020-12-01 10:19:53 +00:00
if '.' not in publishedStr:
if '+' not in publishedStr:
datetimeObject = \
datetime.strptime(publishedStr, "%Y-%m-%dT%H:%M:%SZ")
else:
2020-12-01 10:19:53 +00:00
datetimeObject = \
datetime.strptime(publishedStr.split('+')[0] + 'Z',
"%Y-%m-%dT%H:%M:%SZ")
else:
publishedStr = \
publishedStr.replace('T', ' ').split('.')[0]
datetimeObject = parse(publishedStr)
2021-12-25 20:06:27 +00:00
if not show_published_date_only:
2020-12-01 10:19:53 +00:00
publishedStr = datetimeObject.strftime("%a %b %d, %H:%M")
else:
publishedStr = datetimeObject.strftime("%a %b %d")
# if the post has replies then append a symbol to indicate this
2021-12-25 22:09:19 +00:00
if post_json_object.get('hasReplies'):
if post_json_object['hasReplies'] is True:
2020-12-01 10:19:53 +00:00
publishedStr = '[' + publishedStr + ']'
return publishedStr
2021-12-29 21:55:09 +00:00
def _get_blog_citations_html(boxName: str,
post_json_object: {},
translate: {}) -> str:
2020-11-30 20:52:58 +00:00
"""Returns blog citations as html
"""
# show blog citations
citationsStr = ''
2020-12-01 10:19:53 +00:00
if not (boxName == 'tlblogs' or boxName == 'tlfeatures'):
return citationsStr
2021-12-25 22:09:19 +00:00
if not post_json_object['object'].get('tag'):
2020-12-01 10:19:53 +00:00
return citationsStr
2021-12-25 22:09:19 +00:00
for tagJson in post_json_object['object']['tag']:
2020-12-01 10:19:53 +00:00
if not isinstance(tagJson, dict):
continue
if not tagJson.get('type'):
continue
if tagJson['type'] != 'Article':
continue
if not tagJson.get('name'):
continue
if not tagJson.get('url'):
continue
citationsStr += \
'<li><a href="' + tagJson['url'] + '">' + \
'<cite>' + tagJson['name'] + '</cite></a></li>\n'
if citationsStr:
translatedCitationsStr = 'Citations'
if translate.get(translatedCitationsStr):
translatedCitationsStr = translate[translatedCitationsStr]
citationsStr = '<p><b>' + translatedCitationsStr + ':</b></p>' + \
2020-12-01 10:19:53 +00:00
'<ul>\n' + citationsStr + '</ul>\n'
2020-11-30 20:52:58 +00:00
return citationsStr
2021-12-29 21:55:09 +00:00
def _boost_own_post_html(translate: {}) -> str:
2020-12-01 11:12:38 +00:00
"""The html title for announcing your own post
"""
announcesStr = 'announces'
if translate.get(announcesStr):
announcesStr = translate[announcesStr]
2020-12-01 11:12:38 +00:00
return ' <img loading="lazy" title="' + \
announcesStr + \
'" alt="' + announcesStr + \
2020-12-09 13:08:26 +00:00
'" src="/icons' + \
2020-12-01 11:12:38 +00:00
'/repeat_inactive.png" class="announceOrReply"/>\n'
2021-12-29 21:55:09 +00:00
def _announce_unattributed_html(translate: {},
post_json_object: {}) -> str:
2020-12-01 11:12:38 +00:00
"""Returns the html for an announce title where there
is no attribution on the announced post
"""
announcesStr = 'announces'
if translate.get(announcesStr):
announcesStr = translate[announcesStr]
2021-12-27 11:20:57 +00:00
post_id = remove_id_ending(post_json_object['object']['id'])
2020-12-01 11:12:38 +00:00
return ' <img loading="lazy" title="' + \
announcesStr + '" alt="' + \
announcesStr + '" src="/icons' + \
2020-12-01 11:12:38 +00:00
'/repeat_inactive.png" ' + \
'class="announceOrReply"/>\n' + \
2021-12-26 19:47:06 +00:00
' <a href="' + post_id + \
2020-12-01 11:12:38 +00:00
'" class="announceOrReply">@unattributed</a>\n'
2021-12-29 21:55:09 +00:00
def _announce_with_display_name_html(translate: {},
post_json_object: {},
announceDisplayName: str) -> str:
"""Returns html for an announce having a display name
"""
announcesStr = 'announces'
if translate.get(announcesStr):
announcesStr = translate[announcesStr]
2021-12-27 11:20:57 +00:00
post_id = remove_id_ending(post_json_object['object']['id'])
return ' <img loading="lazy" title="' + \
announcesStr + '" alt="' + \
announcesStr + '" src="/' + \
2020-12-09 13:08:26 +00:00
'icons/repeat_inactive.png" ' + \
'class="announceOrReply"/>\n' + \
2021-12-26 19:47:06 +00:00
' <a href="' + post_id + '" ' + \
'class="announceOrReply">' + announceDisplayName + '</a>\n'
2021-12-29 21:55:09 +00:00
def _get_post_title_announce_html(base_dir: str,
http_prefix: str,
nickname: str, domain: str,
showRepeatIcon: bool,
isAnnounced: bool,
post_json_object: {},
postActor: str,
translate: {},
enableTimingLog: bool,
postStartTime,
boxName: str,
person_cache: {},
allowDownloads: bool,
avatarPosition: str,
pageNumber: int,
messageIdStr: str,
containerClassIcons: str,
containerClass: str) -> (str, str, str, str):
2020-12-01 11:12:38 +00:00
"""Returns the announce title of a post containing names of participants
x announces y
"""
titleStr = ''
replyAvatarImageInPost = ''
2021-12-25 22:09:19 +00:00
objJson = post_json_object['object']
2020-12-01 11:12:38 +00:00
2021-07-21 10:48:24 +00:00
# has no attribution
if not objJson.get('attributedTo'):
2021-12-29 21:55:09 +00:00
titleStr += _announce_unattributed_html(translate, post_json_object)
2021-07-21 10:48:24 +00:00
return (titleStr, replyAvatarImageInPost,
containerClassIcons, containerClass)
attributedTo = ''
if isinstance(objJson['attributedTo'], str):
attributedTo = objJson['attributedTo']
2021-07-21 10:49:54 +00:00
# boosting your own post
2021-07-21 10:48:24 +00:00
if attributedTo.startswith(postActor):
2021-12-29 21:55:09 +00:00
titleStr += _boost_own_post_html(translate)
2021-07-21 10:48:24 +00:00
return (titleStr, replyAvatarImageInPost,
containerClassIcons, containerClass)
# boosting another person's post
2021-12-29 21:55:09 +00:00
_log_post_timing(enableTimingLog, postStartTime, '13.2')
2021-07-21 10:48:24 +00:00
announceNickname = None
if attributedTo:
2021-12-27 22:19:18 +00:00
announceNickname = get_nickname_from_actor(attributedTo)
2021-07-21 10:48:24 +00:00
if not announceNickname:
2021-12-29 21:55:09 +00:00
titleStr += _announce_unattributed_html(translate, post_json_object)
2021-07-21 10:48:24 +00:00
return (titleStr, replyAvatarImageInPost,
containerClassIcons, containerClass)
2021-12-27 19:05:25 +00:00
announceDomain, announcePort = get_domain_from_actor(attributedTo)
2021-12-29 21:55:09 +00:00
get_person_from_cache(base_dir, attributedTo, person_cache, allowDownloads)
2021-12-27 21:59:07 +00:00
announceDisplayName = \
get_display_name(base_dir, attributedTo, person_cache)
2021-07-21 10:48:24 +00:00
if not announceDisplayName:
2021-07-21 19:58:34 +00:00
announceDisplayName = announceNickname + '@' + announceDomain
2021-07-21 10:48:24 +00:00
2021-12-29 21:55:09 +00:00
_log_post_timing(enableTimingLog, postStartTime, '13.3')
2021-07-21 10:48:24 +00:00
# add any emoji to the display name
if ':' in announceDisplayName:
announceDisplayName = \
2021-12-29 21:55:09 +00:00
add_emoji_to_display_name(None, base_dir, http_prefix,
nickname, domain,
announceDisplayName, False)
_log_post_timing(enableTimingLog, postStartTime, '13.3.1')
2021-07-21 10:48:24 +00:00
titleStr += \
2021-12-29 21:55:09 +00:00
_announce_with_display_name_html(translate, post_json_object,
announceDisplayName)
2021-07-21 10:48:24 +00:00
# show avatar of person replied to
2021-07-21 12:05:30 +00:00
announceActor = attributedTo
2021-07-21 10:48:24 +00:00
announceAvatarUrl = \
2021-12-29 21:55:09 +00:00
get_person_avatar_url(base_dir, announceActor,
person_cache, allowDownloads)
2021-07-21 10:48:24 +00:00
2021-12-29 21:55:09 +00:00
_log_post_timing(enableTimingLog, postStartTime, '13.4')
2021-07-21 10:48:24 +00:00
if not announceAvatarUrl:
announceAvatarUrl = ''
2021-07-21 20:38:35 +00:00
idx = 'Show options for this person'
if '/users/news/' not in announceAvatarUrl:
showOptionsForThisPersonStr = idx
if translate.get(idx):
showOptionsForThisPersonStr = translate[idx]
replyAvatarImageInPost = \
' <div class="timeline-avatar-reply">\n' \
' <a class="imageAnchor" ' + \
'href="/users/' + nickname + '?options=' + \
announceActor + ';' + str(pageNumber) + \
';' + announceAvatarUrl + messageIdStr + '">' \
2021-07-21 20:38:35 +00:00
'<img loading="lazy" src="' + announceAvatarUrl + '" ' + \
'title="' + showOptionsForThisPersonStr + \
'" alt=" "' + avatarPosition + \
2021-12-29 21:55:09 +00:00
get_broken_link_substitute() + '/></a>\n </div>\n'
2020-12-01 11:12:38 +00:00
return (titleStr, replyAvatarImageInPost,
containerClassIcons, containerClass)
2021-12-29 21:55:09 +00:00
def _reply_to_yourself_html(translate: {}) -> str:
"""Returns html for a title which is a reply to yourself
"""
replyingToThemselvesStr = 'replying to themselves'
if translate.get(replyingToThemselvesStr):
replyingToThemselvesStr = translate[replyingToThemselvesStr]
return ' <img loading="lazy" title="' + \
replyingToThemselvesStr + \
'" alt="' + replyingToThemselvesStr + \
2020-12-09 13:08:26 +00:00
'" src="/icons' + \
'/reply.png" class="announceOrReply"/>\n'
2021-12-29 21:55:09 +00:00
def _reply_to_unknown_html(translate: {},
post_json_object: {}) -> str:
2020-12-01 13:17:51 +00:00
"""Returns the html title for a reply to an unknown handle
"""
replyingToStr = 'replying to'
if translate.get(replyingToStr):
replyingToStr = translate[replyingToStr]
2020-12-01 13:17:51 +00:00
return ' <img loading="lazy" title="' + \
replyingToStr + '" alt="' + \
replyingToStr + '" src="/icons' + \
2020-12-01 13:17:51 +00:00
'/reply.png" class="announceOrReply"/>\n' + \
' <a href="' + \
2021-12-25 22:09:19 +00:00
post_json_object['object']['inReplyTo'] + \
2020-12-01 13:17:51 +00:00
'" class="announceOrReply">@unknown</a>\n'
2021-12-29 21:55:09 +00:00
def _reply_with_unknown_path_html(translate: {},
post_json_object: {},
postDomain: str) -> str:
"""Returns html title for a reply with an unknown path
eg. does not contain /statuses/
"""
replyingToStr = 'replying to'
if translate.get(replyingToStr):
replyingToStr = translate[replyingToStr]
return ' <img loading="lazy" title="' + \
replyingToStr + \
'" alt="' + replyingToStr + \
2020-12-09 13:08:26 +00:00
'" src="/icons/reply.png" ' + \
'class="announceOrReply"/>\n' + \
' <a href="' + \
2021-12-25 22:09:19 +00:00
post_json_object['object']['inReplyTo'] + \
'" class="announceOrReply">' + \
postDomain + '</a>\n'
2021-12-29 21:55:09 +00:00
def _get_reply_html(translate: {},
inReplyTo: str, replyDisplayName: str) -> str:
2020-12-01 13:32:08 +00:00
"""Returns html title for a reply
"""
replyingToStr = 'replying to'
if translate.get(replyingToStr):
replyingToStr = translate[replyingToStr]
2020-12-01 13:32:08 +00:00
return ' ' + \
'<img loading="lazy" title="' + \
replyingToStr + '" alt="' + \
replyingToStr + '" src="/' + \
2020-12-09 13:08:26 +00:00
'icons/reply.png" ' + \
2020-12-01 13:32:08 +00:00
'class="announceOrReply"/>\n' + \
' <a href="' + inReplyTo + \
'" class="announceOrReply">' + \
replyDisplayName + '</a>\n'
2021-12-29 21:55:09 +00:00
def _get_post_title_reply_html(base_dir: str,
http_prefix: str,
nickname: str, domain: str,
showRepeatIcon: bool,
isAnnounced: bool,
post_json_object: {},
postActor: str,
translate: {},
enableTimingLog: bool,
postStartTime,
boxName: str,
person_cache: {},
allowDownloads: bool,
avatarPosition: str,
pageNumber: int,
messageIdStr: str,
containerClassIcons: str,
containerClass: str) -> (str, str, str, str):
"""Returns the reply title of a post containing names of participants
x replies to y
"""
titleStr = ''
replyAvatarImageInPost = ''
2021-12-25 22:09:19 +00:00
objJson = post_json_object['object']
2021-07-21 10:02:42 +00:00
# not a reply
if not objJson.get('inReplyTo'):
return (titleStr, replyAvatarImageInPost,
containerClassIcons, containerClass)
containerClassIcons = 'containericons darker'
containerClass = 'container darker'
2021-07-21 10:02:42 +00:00
# reply to self
if objJson['inReplyTo'].startswith(postActor):
2021-12-29 21:55:09 +00:00
titleStr += _reply_to_yourself_html(translate)
return (titleStr, replyAvatarImageInPost,
containerClassIcons, containerClass)
2021-07-21 10:02:42 +00:00
# has a reply
2021-07-21 12:05:30 +00:00
if '/statuses/' not in objJson['inReplyTo']:
2021-07-21 10:02:42 +00:00
postDomain = objJson['inReplyTo']
2021-12-27 17:20:01 +00:00
prefixes = get_protocol_prefixes()
for prefix in prefixes:
postDomain = postDomain.replace(prefix, '')
if '/' in postDomain:
postDomain = postDomain.split('/', 1)[0]
if postDomain:
titleStr += \
2021-12-29 21:55:09 +00:00
_reply_with_unknown_path_html(translate,
post_json_object, postDomain)
2021-07-21 12:05:30 +00:00
return (titleStr, replyAvatarImageInPost,
containerClassIcons, containerClass)
inReplyTo = objJson['inReplyTo']
replyActor = inReplyTo.split('/statuses/')[0]
2021-12-27 22:19:18 +00:00
replyNickname = get_nickname_from_actor(replyActor)
2021-07-21 12:05:30 +00:00
if not replyNickname:
2021-12-29 21:55:09 +00:00
titleStr += _reply_to_unknown_html(translate, post_json_object)
2021-07-21 12:05:30 +00:00
return (titleStr, replyAvatarImageInPost,
containerClassIcons, containerClass)
2021-12-27 19:05:25 +00:00
replyDomain, replyPort = get_domain_from_actor(replyActor)
2021-07-21 12:05:30 +00:00
if not (replyNickname and replyDomain):
2021-12-29 21:55:09 +00:00
titleStr += _reply_to_unknown_html(translate, post_json_object)
2021-07-21 12:05:30 +00:00
return (titleStr, replyAvatarImageInPost,
containerClassIcons, containerClass)
2021-12-29 21:55:09 +00:00
get_person_from_cache(base_dir, replyActor, person_cache, allowDownloads)
2021-12-27 21:59:07 +00:00
replyDisplayName = get_display_name(base_dir, replyActor, person_cache)
2021-07-21 12:05:30 +00:00
if not replyDisplayName:
2021-07-21 19:58:34 +00:00
replyDisplayName = replyNickname + '@' + replyDomain
2021-07-21 12:05:30 +00:00
# add emoji to the display name
if ':' in replyDisplayName:
2021-12-29 21:55:09 +00:00
_log_post_timing(enableTimingLog, postStartTime, '13.5')
2021-07-21 12:05:30 +00:00
replyDisplayName = \
2021-12-29 21:55:09 +00:00
add_emoji_to_display_name(None, base_dir, http_prefix,
nickname, domain,
replyDisplayName, False)
_log_post_timing(enableTimingLog, postStartTime, '13.6')
2021-07-21 12:05:30 +00:00
2021-12-29 21:55:09 +00:00
titleStr += _get_reply_html(translate, inReplyTo, replyDisplayName)
2021-07-21 12:05:30 +00:00
2021-12-29 21:55:09 +00:00
_log_post_timing(enableTimingLog, postStartTime, '13.7')
2021-07-21 12:05:30 +00:00
# show avatar of person replied to
replyAvatarUrl = \
2021-12-29 21:55:09 +00:00
get_person_avatar_url(base_dir, replyActor, person_cache,
allowDownloads)
2021-07-21 12:05:30 +00:00
2021-12-29 21:55:09 +00:00
_log_post_timing(enableTimingLog, postStartTime, '13.8')
2021-07-21 12:05:30 +00:00
if replyAvatarUrl:
showProfileStr = 'Show profile'
if translate.get(showProfileStr):
showProfileStr = translate[showProfileStr]
2021-07-21 12:05:30 +00:00
replyAvatarImageInPost = \
' <div class="timeline-avatar-reply">\n' + \
' <a class="imageAnchor" ' + \
'href="/users/' + nickname + '?options=' + replyActor + \
';' + str(pageNumber) + ';' + replyAvatarUrl + \
messageIdStr + '">\n' + \
' <img loading="lazy" src="' + replyAvatarUrl + '" ' + \
'title="' + showProfileStr + \
2021-12-29 21:55:09 +00:00
'" alt=" "' + avatarPosition + get_broken_link_substitute() + \
2021-07-21 12:05:30 +00:00
'/></a>\n </div>\n'
return (titleStr, replyAvatarImageInPost,
containerClassIcons, containerClass)
2021-12-29 21:55:09 +00:00
def _get_post_title_html(base_dir: str,
http_prefix: str,
nickname: str, domain: str,
showRepeatIcon: bool,
isAnnounced: bool,
post_json_object: {},
postActor: str,
translate: {},
enableTimingLog: bool,
postStartTime,
boxName: str,
person_cache: {},
allowDownloads: bool,
avatarPosition: str,
pageNumber: int,
messageIdStr: str,
containerClassIcons: str,
containerClass: str) -> (str, str, str, str):
2020-11-30 23:23:21 +00:00
"""Returns the title of a post containing names of participants
x replies to y, x announces y, etc
2020-11-09 19:42:09 +00:00
"""
2021-07-21 21:23:19 +00:00
if not isAnnounced and boxName == 'search' and \
2021-12-25 22:09:19 +00:00
post_json_object.get('object'):
if post_json_object['object'].get('attributedTo'):
if post_json_object['object']['attributedTo'] != postActor:
2021-07-21 21:23:19 +00:00
isAnnounced = True
2020-12-01 10:30:24 +00:00
if isAnnounced:
2021-12-29 21:55:09 +00:00
return _get_post_title_announce_html(base_dir,
http_prefix,
nickname, domain,
showRepeatIcon,
isAnnounced,
post_json_object,
postActor,
translate,
enableTimingLog,
postStartTime,
boxName,
person_cache,
allowDownloads,
avatarPosition,
pageNumber,
messageIdStr,
containerClassIcons,
containerClass)
return _get_post_title_reply_html(base_dir,
http_prefix,
nickname, domain,
showRepeatIcon,
isAnnounced,
post_json_object,
postActor,
translate,
enableTimingLog,
postStartTime,
boxName,
person_cache,
allowDownloads,
avatarPosition,
pageNumber,
messageIdStr,
containerClassIcons,
containerClass)
def _get_footer_with_icons(showIcons: bool,
containerClassIcons: str,
replyStr: str, announceStr: str,
likeStr: str, reactionStr: str,
bookmarkStr: str,
deleteStr: str, muteStr: str, editStr: str,
post_json_object: {}, publishedLink: str,
timeClass: str, publishedStr: str) -> str:
2020-12-01 14:08:12 +00:00
"""Returns the html for a post footer containing icons
"""
if not showIcons:
return None
2020-12-27 20:33:56 +00:00
footerStr = '\n <nav>\n'
footerStr += ' <div class="' + containerClassIcons + '">\n'
2021-11-11 15:02:03 +00:00
footerStr += replyStr + announceStr + likeStr + bookmarkStr + reactionStr
2020-12-01 14:11:50 +00:00
footerStr += deleteStr + muteStr + editStr
2021-12-28 12:20:18 +00:00
if not is_news_post(post_json_object):
2020-12-01 14:08:12 +00:00
footerStr += ' <a href="' + publishedLink + '" class="' + \
timeClass + '">' + publishedStr + '</a>\n'
else:
footerStr += ' <a href="' + \
publishedLink.replace('/news/', '/news/statuses/') + \
2020-12-01 14:11:50 +00:00
'" class="' + timeClass + '">' + publishedStr + '</a>\n'
2020-12-01 14:08:12 +00:00
footerStr += ' </div>\n'
2020-12-27 20:33:56 +00:00
footerStr += ' </nav>\n'
2020-12-01 14:08:12 +00:00
return footerStr
2021-12-29 21:55:09 +00:00
def individual_post_as_html(signing_priv_key_pem: str,
allowDownloads: bool,
recent_posts_cache: {}, max_recent_posts: int,
translate: {},
pageNumber: int, base_dir: str,
session, cached_webfingers: {}, person_cache: {},
nickname: str, domain: str, port: int,
post_json_object: {},
avatarUrl: str, showAvatarOptions: bool,
allow_deletion: bool,
http_prefix: str, project_version: str,
boxName: str,
yt_replace_domain: str,
twitter_replacement_domain: str,
show_published_date_only: bool,
peertube_instances: [],
allow_local_network_access: bool,
theme_name: str, system_language: str,
max_like_count: int,
showRepeats: bool,
showIcons: bool,
manuallyApprovesFollowers: bool,
showPublicOnly: bool,
storeToCache: bool,
useCacheOnly: bool,
cw_lists: {},
lists_enabled: str) -> str:
2020-11-30 23:23:21 +00:00
""" Shows a single post as html
"""
2021-12-25 22:09:19 +00:00
if not post_json_object:
2020-11-30 23:23:21 +00:00
return ''
2021-11-10 17:35:54 +00:00
# maximum number of different emoji reactions which can be added to a post
maxReactionTypes = 5
2020-11-30 23:23:21 +00:00
# benchmark
postStartTime = time.time()
2021-12-25 22:09:19 +00:00
postActor = post_json_object['actor']
2020-11-30 23:23:21 +00:00
# ZZZzzz
2021-12-29 21:55:09 +00:00
if is_person_snoozed(base_dir, nickname, domain, postActor):
2020-11-30 23:23:21 +00:00
return ''
# if downloads of avatar images aren't enabled then we can do more
# accurate timing of different parts of the code
enableTimingLog = not allowDownloads
2021-12-29 21:55:09 +00:00
_log_post_timing(enableTimingLog, postStartTime, '1')
2020-11-30 23:23:21 +00:00
avatarPosition = ''
messageId = ''
2021-12-25 22:09:19 +00:00
if post_json_object.get('id'):
2021-12-27 17:16:57 +00:00
messageId = remove_hash_from_post_id(post_json_object['id'])
2021-12-27 11:20:57 +00:00
messageId = remove_id_ending(messageId)
2020-11-30 23:23:21 +00:00
2021-12-29 21:55:09 +00:00
_log_post_timing(enableTimingLog, postStartTime, '2')
2020-11-30 23:23:21 +00:00
messageIdStr = ''
if messageId:
messageIdStr = ';' + messageId
2020-11-09 19:42:09 +00:00
2021-12-26 12:45:03 +00:00
domain_full = get_full_domain(domain, port)
2020-11-09 19:42:09 +00:00
pageNumberParam = ''
if pageNumber:
pageNumberParam = '?page=' + str(pageNumber)
2020-11-30 14:45:12 +00:00
# get the html post from the recent posts cache if it exists there
2020-11-30 12:47:52 +00:00
postHtml = \
2021-12-29 21:55:09 +00:00
_get_post_from_recent_cache(session, base_dir,
http_prefix, nickname, domain,
post_json_object,
postActor,
person_cache,
allowDownloads,
showPublicOnly,
storeToCache,
boxName,
avatarUrl,
enableTimingLog,
postStartTime,
pageNumber,
recent_posts_cache,
max_recent_posts,
signing_priv_key_pem)
2020-11-30 12:47:52 +00:00
if postHtml:
2021-09-28 14:15:04 +00:00
return postHtml
2021-12-25 22:09:19 +00:00
if useCacheOnly and post_json_object['type'] != 'Announce':
return ''
2020-11-09 19:42:09 +00:00
2021-12-29 21:55:09 +00:00
_log_post_timing(enableTimingLog, postStartTime, '4')
2020-11-09 19:42:09 +00:00
2020-11-30 14:45:12 +00:00
avatarUrl = \
2021-12-29 21:55:09 +00:00
get_avatar_image_url(session,
base_dir, http_prefix,
postActor, person_cache,
avatarUrl, allowDownloads,
signing_priv_key_pem)
2020-11-09 19:42:09 +00:00
2021-12-29 21:55:09 +00:00
_log_post_timing(enableTimingLog, postStartTime, '5')
2020-11-09 19:42:09 +00:00
2020-11-30 15:20:10 +00:00
# get the display name
2021-12-26 10:00:46 +00:00
if domain_full not in postActor:
2021-01-09 22:58:34 +00:00
# lookup the correct webfinger for the postActor
2021-12-27 22:19:18 +00:00
postActorNickname = get_nickname_from_actor(postActor)
2021-12-27 19:05:25 +00:00
postActorDomain, postActorPort = get_domain_from_actor(postActor)
2021-12-26 12:45:03 +00:00
postActorDomainFull = get_full_domain(postActorDomain, postActorPort)
2021-01-09 22:58:34 +00:00
postActorHandle = postActorNickname + '@' + postActorDomainFull
2021-01-10 10:34:29 +00:00
postActorWf = \
2021-12-29 21:55:09 +00:00
webfinger_handle(session, postActorHandle, http_prefix,
cached_webfingers,
domain, __version__, False, False,
signing_priv_key_pem)
2021-01-09 22:58:34 +00:00
2021-01-10 10:34:29 +00:00
avatarUrl2 = None
displayName = None
if postActorWf:
2021-09-15 14:05:08 +00:00
originDomain = domain
(inboxUrl, pubKeyId, pubKey, fromPersonId, sharedInbox, avatarUrl2,
2021-12-29 21:55:09 +00:00
displayName, _) = get_person_box(signing_priv_key_pem,
originDomain,
base_dir, session,
postActorWf,
person_cache,
project_version,
http_prefix,
nickname, domain,
'outbox', 72367)
_log_post_timing(enableTimingLog, postStartTime, '6')
2020-11-09 19:42:09 +00:00
if avatarUrl2:
avatarUrl = avatarUrl2
if displayName:
2020-11-30 15:20:10 +00:00
# add any emoji to the display name
2020-11-09 19:42:09 +00:00
if ':' in displayName:
displayName = \
2021-12-29 21:55:09 +00:00
add_emoji_to_display_name(session, base_dir, http_prefix,
nickname, domain,
displayName, False)
2020-11-09 19:42:09 +00:00
2021-12-29 21:55:09 +00:00
_log_post_timing(enableTimingLog, postStartTime, '7')
2020-11-09 19:42:09 +00:00
2020-11-30 15:20:10 +00:00
avatarLink = \
2021-12-29 21:55:09 +00:00
_get_avatar_image_html(showAvatarOptions,
nickname, domain_full,
avatarUrl, postActor,
translate, avatarPosition,
pageNumber, messageIdStr)
2020-11-09 19:42:09 +00:00
avatarImageInPost = \
2020-11-30 15:20:10 +00:00
' <div class="timeline-avatar">' + avatarLink + '</div>\n'
2020-11-09 19:42:09 +00:00
2021-12-27 11:20:57 +00:00
timelinePostBookmark = remove_id_ending(post_json_object['id'])
2020-11-09 19:42:09 +00:00
timelinePostBookmark = timelinePostBookmark.replace('://', '-')
timelinePostBookmark = timelinePostBookmark.replace('/', '-')
# If this is the inbox timeline then don't show the repeat icon on any DMs
showRepeatIcon = showRepeats
isPublicRepeat = False
2021-12-26 20:12:18 +00:00
postIsDM = is_dm(post_json_object)
2020-11-09 19:42:09 +00:00
if showRepeats:
2021-02-24 12:55:34 +00:00
if postIsDM:
2020-11-09 19:42:09 +00:00
showRepeatIcon = False
else:
2021-12-28 14:41:10 +00:00
if not is_public_post(post_json_object):
2020-11-09 19:42:09 +00:00
isPublicRepeat = True
titleStr = ''
galleryStr = ''
isAnnounced = False
announceJsonObject = None
2021-12-25 22:09:19 +00:00
if post_json_object['type'] == 'Announce':
announceJsonObject = post_json_object.copy()
blockedCache = {}
2021-12-29 21:55:09 +00:00
post_jsonAnnounce = \
download_announce(session, base_dir, http_prefix,
nickname, domain, post_json_object,
project_version, translate,
yt_replace_domain,
twitter_replacement_domain,
allow_local_network_access,
recent_posts_cache, False,
system_language,
domain_full, person_cache,
signing_priv_key_pem,
blockedCache)
if not post_jsonAnnounce:
# if the announce could not be downloaded then mark it as rejected
2021-12-27 11:20:57 +00:00
announcedPostId = remove_id_ending(post_json_object['id'])
2021-12-26 20:20:36 +00:00
reject_post_id(base_dir, nickname, domain, announcedPostId,
recent_posts_cache)
2020-11-09 19:42:09 +00:00
return ''
2021-12-29 21:55:09 +00:00
post_json_object = post_jsonAnnounce
2021-03-03 19:27:29 +00:00
2021-09-17 17:58:20 +00:00
# is the announced post in the html cache?
postHtml = \
2021-12-29 21:55:09 +00:00
_get_post_from_recent_cache(session, base_dir,
http_prefix, nickname, domain,
post_json_object,
postActor,
person_cache,
allowDownloads,
showPublicOnly,
storeToCache,
boxName,
avatarUrl,
enableTimingLog,
postStartTime,
pageNumber,
recent_posts_cache,
max_recent_posts,
signing_priv_key_pem)
2021-09-17 17:58:20 +00:00
if postHtml:
2021-09-28 14:15:04 +00:00
return postHtml
2021-09-17 17:58:20 +00:00
announceFilename = \
2021-12-26 20:36:08 +00:00
locate_post(base_dir, nickname, domain, post_json_object['id'])
if announceFilename:
2021-12-26 23:41:34 +00:00
update_announce_collection(recent_posts_cache,
base_dir, announceFilename,
postActor, nickname,
domain_full, False)
2021-05-07 21:10:55 +00:00
# create a file for use by text-to-speech
2021-12-26 20:43:03 +00:00
if is_recent_post(post_json_object, 3):
2021-12-25 22:09:19 +00:00
if post_json_object.get('actor'):
if not os.path.isfile(announceFilename + '.tts'):
2021-12-29 21:55:09 +00:00
update_speaker(base_dir, http_prefix,
nickname, domain, domain_full,
post_json_object, person_cache,
translate, post_json_object['actor'],
theme_name)
2021-06-22 12:27:10 +00:00
with open(announceFilename + '.tts', 'w+') as ttsFile:
ttsFile.write('\n')
2021-03-03 19:27:29 +00:00
2020-11-09 19:42:09 +00:00
isAnnounced = True
2021-12-29 21:55:09 +00:00
_log_post_timing(enableTimingLog, postStartTime, '8')
2020-11-09 19:42:09 +00:00
2021-12-26 10:57:03 +00:00
if not has_object_dict(post_json_object):
2020-11-09 19:42:09 +00:00
return ''
# if this post should be public then check its recipients
if showPublicOnly:
2021-12-29 21:55:09 +00:00
if not post_contains_public(post_json_object):
2020-11-09 19:42:09 +00:00
return ''
isModerationPost = False
2021-12-25 22:09:19 +00:00
if post_json_object['object'].get('moderationStatus'):
2020-11-09 19:42:09 +00:00
isModerationPost = True
containerClass = 'container'
containerClassIcons = 'containericons'
timeClass = 'time-right'
2021-12-27 22:19:18 +00:00
actorNickname = get_nickname_from_actor(postActor)
2020-11-09 19:42:09 +00:00
if not actorNickname:
# single user instance
actorNickname = 'dev'
2021-12-27 19:05:25 +00:00
actorDomain, actorPort = get_domain_from_actor(postActor)
2020-11-09 19:42:09 +00:00
2021-12-27 21:59:07 +00:00
displayName = get_display_name(base_dir, postActor, person_cache)
2020-11-09 19:42:09 +00:00
if displayName:
if ':' in displayName:
displayName = \
2021-12-29 21:55:09 +00:00
add_emoji_to_display_name(session, base_dir, http_prefix,
nickname, domain,
displayName, False)
2020-11-09 19:42:09 +00:00
titleStr += \
' <a class="imageAnchor" href="/users/' + \
nickname + '?options=' + postActor + \
';' + str(pageNumber) + ';' + avatarUrl + messageIdStr + \
'">' + displayName + '</a>\n'
else:
if not messageId:
2021-12-25 22:09:19 +00:00
# pprint(post_json_object)
2020-11-09 19:42:09 +00:00
print('ERROR: no messageId')
if not actorNickname:
2021-12-25 22:09:19 +00:00
# pprint(post_json_object)
2020-11-09 19:42:09 +00:00
print('ERROR: no actorNickname')
if not actorDomain:
2021-12-25 22:09:19 +00:00
# pprint(post_json_object)
2020-11-09 19:42:09 +00:00
print('ERROR: no actorDomain')
titleStr += \
' <a class="imageAnchor" href="/users/' + \
nickname + '?options=' + postActor + \
';' + str(pageNumber) + ';' + avatarUrl + messageIdStr + \
'">@' + actorNickname + '@' + actorDomain + '</a>\n'
# benchmark 9
2021-12-29 21:55:09 +00:00
_log_post_timing(enableTimingLog, postStartTime, '9')
2020-11-09 19:42:09 +00:00
# Show a DM icon for DMs in the inbox timeline
2021-02-24 12:55:34 +00:00
if postIsDM:
2020-11-09 19:42:09 +00:00
titleStr = \
titleStr + ' <img loading="lazy" src="/' + \
2020-12-09 13:08:26 +00:00
'icons/dm.png" class="DMicon"/>\n'
2020-11-09 19:42:09 +00:00
# check if replying is permitted
commentsEnabled = True
2021-12-25 22:09:19 +00:00
if isinstance(post_json_object['object'], dict) and \
'commentsEnabled' in post_json_object['object']:
if post_json_object['object']['commentsEnabled'] is False:
2020-11-09 19:42:09 +00:00
commentsEnabled = False
2021-12-25 22:09:19 +00:00
elif 'rejectReplies' in post_json_object['object']:
if post_json_object['object']['rejectReplies']:
commentsEnabled = False
2020-11-09 19:42:09 +00:00
2021-08-08 16:52:32 +00:00
conversationId = None
2021-12-25 22:09:19 +00:00
if isinstance(post_json_object['object'], dict) and \
'conversation' in post_json_object['object']:
if post_json_object['object']['conversation']:
conversationId = post_json_object['object']['conversation']
2021-08-08 16:52:32 +00:00
2021-12-06 16:53:21 +00:00
publicReply = False
2021-12-28 14:41:10 +00:00
if is_public_post(post_json_object):
2021-12-06 16:53:21 +00:00
publicReply = True
2021-12-29 21:55:09 +00:00
replyStr = _get_reply_icon_html(base_dir, nickname, domain,
publicReply,
showIcons, commentsEnabled,
post_json_object, pageNumberParam,
translate, system_language,
conversationId)
2020-11-09 19:42:09 +00:00
2021-12-29 21:55:09 +00:00
_log_post_timing(enableTimingLog, postStartTime, '10')
2020-11-09 19:42:09 +00:00
2021-12-29 21:55:09 +00:00
editStr = _get_edit_icon_html(base_dir, nickname, domain_full,
post_json_object, actorNickname,
translate, False)
2021-12-29 21:55:09 +00:00
_log_post_timing(enableTimingLog, postStartTime, '11')
2020-11-30 20:52:58 +00:00
announceStr = \
2021-12-29 21:55:09 +00:00
_get_announce_icon_html(isAnnounced,
postActor,
nickname, domain_full,
announceJsonObject,
post_json_object,
isPublicRepeat,
isModerationPost,
showRepeatIcon,
translate,
pageNumberParam,
timelinePostBookmark,
boxName)
_log_post_timing(enableTimingLog, postStartTime, '12')
2020-11-09 19:42:09 +00:00
# whether to show a like button
hideLikeButtonFile = \
2021-12-26 12:02:29 +00:00
acct_dir(base_dir, nickname, domain) + '/.hideLikeButton'
2020-11-09 19:42:09 +00:00
showLikeButton = True
if os.path.isfile(hideLikeButtonFile):
showLikeButton = False
2021-11-17 14:25:24 +00:00
# whether to show a reaction button
hideReactionButtonFile = \
2021-12-26 12:02:29 +00:00
acct_dir(base_dir, nickname, domain) + '/.hideReactionButton'
2021-11-17 14:25:24 +00:00
showReactionButton = True
if os.path.isfile(hideReactionButtonFile):
showReactionButton = False
2021-12-25 22:09:19 +00:00
likeJsonObject = post_json_object
2021-10-13 17:40:42 +00:00
if announceJsonObject:
likeJsonObject = announceJsonObject
2021-12-29 21:55:09 +00:00
likeStr = _get_like_icon_html(nickname, domain_full,
isModerationPost,
showLikeButton,
likeJsonObject,
enableTimingLog,
postStartTime,
translate, pageNumberParam,
timelinePostBookmark,
boxName, max_like_count)
2020-11-09 19:42:09 +00:00
2021-12-29 21:55:09 +00:00
_log_post_timing(enableTimingLog, postStartTime, '12.5')
2020-11-09 19:42:09 +00:00
bookmarkStr = \
2021-12-29 21:55:09 +00:00
_get_bookmark_icon_html(nickname, domain_full,
post_json_object,
isModerationPost,
translate,
enableTimingLog,
postStartTime, boxName,
pageNumberParam,
timelinePostBookmark)
2020-11-09 19:42:09 +00:00
2021-12-29 21:55:09 +00:00
_log_post_timing(enableTimingLog, postStartTime, '12.9')
2020-11-09 19:42:09 +00:00
2021-11-11 15:02:03 +00:00
reactionStr = \
2021-12-29 21:55:09 +00:00
_get_reaction_icon_html(nickname, domain_full,
post_json_object,
isModerationPost,
showReactionButton,
translate,
enableTimingLog,
postStartTime, boxName,
pageNumberParam,
timelinePostBookmark)
2021-11-11 15:02:03 +00:00
2021-12-29 21:55:09 +00:00
_log_post_timing(enableTimingLog, postStartTime, '12.10')
2021-11-11 15:02:03 +00:00
2021-12-29 21:55:09 +00:00
is_muted = post_is_muted(base_dir, nickname, domain,
post_json_object, messageId)
2020-11-09 19:42:09 +00:00
2021-12-29 21:55:09 +00:00
_log_post_timing(enableTimingLog, postStartTime, '13')
2020-11-09 19:42:09 +00:00
2020-11-30 17:48:35 +00:00
muteStr = \
2021-12-29 21:55:09 +00:00
_get_mute_icon_html(is_muted,
postActor,
messageId,
nickname, domain_full,
allow_deletion,
pageNumberParam,
boxName,
timelinePostBookmark,
translate)
2020-11-30 17:48:35 +00:00
deleteStr = \
2021-12-29 21:55:09 +00:00
_get_delete_icon_html(nickname, domain_full,
allow_deletion,
postActor,
messageId,
post_json_object,
pageNumberParam,
translate)
2020-11-09 19:42:09 +00:00
2021-12-29 21:55:09 +00:00
_log_post_timing(enableTimingLog, postStartTime, '13.1')
2020-11-09 19:42:09 +00:00
2020-11-30 23:23:21 +00:00
# get the title: x replies to y, x announces y, etc
(titleStr2,
replyAvatarImageInPost,
containerClassIcons,
2021-12-29 21:55:09 +00:00
containerClass) = _get_post_title_html(base_dir,
http_prefix,
nickname, domain,
showRepeatIcon,
isAnnounced,
post_json_object,
postActor,
translate,
enableTimingLog,
postStartTime,
boxName,
person_cache,
allowDownloads,
avatarPosition,
pageNumber,
messageIdStr,
containerClassIcons,
containerClass)
2020-11-30 23:23:21 +00:00
titleStr += titleStr2
2020-11-09 19:42:09 +00:00
2021-12-29 21:55:09 +00:00
_log_post_timing(enableTimingLog, postStartTime, '14')
2020-11-09 19:42:09 +00:00
attachmentStr, galleryStr = \
2021-12-29 21:55:09 +00:00
get_post_attachments_as_html(post_json_object, boxName, translate,
is_muted, avatarLink,
replyStr, announceStr, likeStr,
bookmarkStr, deleteStr, muteStr)
2020-11-09 19:42:09 +00:00
publishedStr = \
2021-12-29 21:55:09 +00:00
_get_published_date_str(post_json_object, show_published_date_only)
2020-11-09 19:42:09 +00:00
2021-12-29 21:55:09 +00:00
_log_post_timing(enableTimingLog, postStartTime, '15')
2020-11-09 19:42:09 +00:00
publishedLink = messageId
# blog posts should have no /statuses/ in their link
2021-12-28 13:49:44 +00:00
if is_blog_post(post_json_object):
2020-11-09 19:42:09 +00:00
# is this a post to the local domain?
if '://' + domain in messageId:
publishedLink = messageId.replace('/statuses/', '/')
# if this is a local link then make it relative so that it works
# on clearnet or onion address
if domain + '/users/' in publishedLink or \
domain + ':' + str(port) + '/users/' in publishedLink:
publishedLink = '/users/' + publishedLink.split('/users/')[1]
2021-12-28 12:20:18 +00:00
if not is_news_post(post_json_object):
2020-11-09 19:42:09 +00:00
footerStr = '<a href="' + publishedLink + \
'" class="' + timeClass + '">' + publishedStr + '</a>\n'
else:
footerStr = '<a href="' + \
publishedLink.replace('/news/', '/news/statuses/') + \
'" class="' + timeClass + '">' + publishedStr + '</a>\n'
# change the background color for DMs in inbox timeline
2021-02-24 12:55:34 +00:00
if postIsDM:
2020-11-09 19:42:09 +00:00
containerClassIcons = 'containericons dm'
containerClass = 'container dm'
2021-12-29 21:55:09 +00:00
newFooterStr = _get_footer_with_icons(showIcons,
containerClassIcons,
replyStr, announceStr,
likeStr, reactionStr, bookmarkStr,
deleteStr, muteStr, editStr,
post_json_object, publishedLink,
timeClass, publishedStr)
2020-12-01 14:08:12 +00:00
if newFooterStr:
footerStr = newFooterStr
2020-11-09 19:42:09 +00:00
2021-10-21 13:08:21 +00:00
# add any content warning from the cwlists directory
2021-12-30 10:16:57 +00:00
add_cw_from_lists(post_json_object, cw_lists, translate, lists_enabled)
2021-10-21 13:08:21 +00:00
2020-11-09 19:42:09 +00:00
postIsSensitive = False
2021-12-25 22:09:19 +00:00
if post_json_object['object'].get('sensitive'):
2020-11-09 19:42:09 +00:00
# sensitive posts should have a summary
2021-12-25 22:09:19 +00:00
if post_json_object['object'].get('summary'):
postIsSensitive = post_json_object['object']['sensitive']
2020-11-09 19:42:09 +00:00
else:
# add a generic summary if none is provided
sensitiveStr = 'Sensitive'
if translate.get(sensitiveStr):
sensitiveStr = translate[sensitiveStr]
2021-12-25 22:09:19 +00:00
post_json_object['object']['summary'] = sensitiveStr
2020-11-09 19:42:09 +00:00
# add an extra line if there is a content warning,
# for better vertical spacing on mobile
if postIsSensitive:
footerStr = '<br>' + footerStr
2021-12-25 22:09:19 +00:00
if not post_json_object['object'].get('summary'):
post_json_object['object']['summary'] = ''
2020-11-09 19:42:09 +00:00
2021-12-25 22:09:19 +00:00
if post_json_object['object'].get('cipherText'):
post_json_object['object']['content'] = \
2021-12-29 21:55:09 +00:00
e2e_edecrypt_message_from_device(post_json_object['object'])
2021-12-25 23:03:28 +00:00
post_json_object['object']['contentMap'][system_language] = \
2021-12-25 22:09:19 +00:00
post_json_object['object']['content']
2020-11-09 19:42:09 +00:00
2021-12-26 12:45:03 +00:00
domain_full = get_full_domain(domain, port)
2021-12-26 10:19:59 +00:00
personUrl = local_actor_url(http_prefix, nickname, domain_full)
2021-12-26 10:29:52 +00:00
actor_json = \
2021-12-29 21:55:09 +00:00
get_person_from_cache(base_dir, personUrl, person_cache, False)
2021-12-26 10:52:54 +00:00
languages_understood = []
2021-12-26 10:29:52 +00:00
if actor_json:
2021-12-26 10:52:54 +00:00
languages_understood = get_actor_languages_list(actor_json)
2021-12-26 10:50:49 +00:00
contentStr = get_content_from_post(post_json_object, system_language,
2021-12-26 10:52:54 +00:00
languages_understood)
if not contentStr:
2021-07-20 11:21:15 +00:00
contentStr = \
2021-12-29 21:55:09 +00:00
auto_translate_post(base_dir, post_json_object,
system_language, translate)
2021-07-19 19:40:04 +00:00
if not contentStr:
return ''
2020-11-09 19:42:09 +00:00
2021-12-29 21:55:09 +00:00
isPatch = is_git_patch(base_dir, nickname, domain,
post_json_object['object']['type'],
post_json_object['object']['summary'],
contentStr)
2020-11-09 19:42:09 +00:00
2021-12-29 21:55:09 +00:00
_log_post_timing(enableTimingLog, postStartTime, '16')
2020-11-09 19:42:09 +00:00
2021-12-26 19:15:36 +00:00
if not is_pgp_encrypted(contentStr):
if not isPatch:
objectContent = \
2021-12-29 21:55:09 +00:00
remove_long_words(contentStr, 40, [])
objectContent = remove_text_formatting(objectContent)
objectContent = limit_repeated_words(objectContent, 6)
objectContent = \
2021-12-29 21:55:09 +00:00
switch_words(base_dir, nickname, domain, objectContent)
objectContent = html_replace_email_quote(objectContent)
objectContent = html_replace_quote_marks(objectContent)
else:
objectContent = contentStr
2020-11-09 19:42:09 +00:00
else:
encryptedStr = 'Encrypted'
if translate.get(encryptedStr):
encryptedStr = translate[encryptedStr]
objectContent = '🔒 ' + encryptedStr
2020-11-09 19:42:09 +00:00
2020-12-27 14:04:48 +00:00
objectContent = '<article>' + objectContent + '</article>'
2020-11-09 19:42:09 +00:00
if not postIsSensitive:
contentStr = objectContent + attachmentStr
2021-12-29 21:55:09 +00:00
contentStr = add_embedded_elements(translate, contentStr,
peertube_instances)
contentStr = insert_question(base_dir, translate,
nickname, domain, port,
contentStr, post_json_object,
pageNumber)
2020-11-09 19:42:09 +00:00
else:
2021-12-28 21:36:27 +00:00
postID = 'post' + str(create_password(8))
2020-11-09 19:42:09 +00:00
contentStr = ''
2021-12-25 22:09:19 +00:00
if post_json_object['object'].get('summary'):
cwStr = str(post_json_object['object']['summary'])
2020-12-29 09:52:52 +00:00
cwStr = \
2021-12-29 21:55:09 +00:00
add_emoji_to_display_name(session, base_dir, http_prefix,
nickname, domain,
cwStr, False)
2021-01-19 19:17:20 +00:00
contentStr += \
2021-01-19 19:24:16 +00:00
'<label class="cw">' + cwStr + '</label>\n '
2020-11-09 19:42:09 +00:00
if isModerationPost:
containerClass = 'container report'
# get the content warning text
cwContentStr = objectContent + attachmentStr
if not isPatch:
2021-12-29 21:55:09 +00:00
cwContentStr = add_embedded_elements(translate, cwContentStr,
peertube_instances)
2020-11-09 19:42:09 +00:00
cwContentStr = \
2021-12-29 21:55:09 +00:00
insert_question(base_dir, translate, nickname, domain, port,
cwContentStr, post_json_object, pageNumber)
cwContentStr = \
2021-12-29 21:55:09 +00:00
switch_words(base_dir, nickname, domain, cwContentStr)
2021-12-28 13:49:44 +00:00
if not is_blog_post(post_json_object):
2020-11-09 19:42:09 +00:00
# get the content warning button
contentStr += \
2021-12-29 21:55:09 +00:00
get_content_warning_button(postID, translate, cwContentStr)
2020-11-09 19:42:09 +00:00
else:
contentStr += cwContentStr
2021-12-29 21:55:09 +00:00
_log_post_timing(enableTimingLog, postStartTime, '17')
2020-11-09 19:42:09 +00:00
2021-12-25 22:09:19 +00:00
if post_json_object['object'].get('tag') and not isPatch:
2020-11-09 19:42:09 +00:00
contentStr = \
2021-12-29 21:55:09 +00:00
replace_emoji_from_tags(session, base_dir, contentStr,
post_json_object['object']['tag'],
'content', False)
2020-11-09 19:42:09 +00:00
2021-12-29 21:55:09 +00:00
if is_muted:
2020-11-09 19:42:09 +00:00
contentStr = ''
else:
if not isPatch:
contentStr = ' <div class="message">' + \
contentStr + \
' </div>\n'
else:
contentStr = \
'<div class="gitpatch"><pre><code>' + contentStr + \
'</code></pre></div>\n'
# show blog citations
2020-11-30 20:52:58 +00:00
citationsStr = \
2021-12-29 21:55:09 +00:00
_get_blog_citations_html(boxName, post_json_object, translate)
2020-11-09 19:42:09 +00:00
postHtml = ''
if boxName != 'tlmedia':
2021-11-10 17:14:51 +00:00
reactionStr = ''
if showIcons:
reactionStr = \
2021-12-29 21:55:09 +00:00
html_emoji_reactions(post_json_object, True, personUrl,
maxReactionTypes,
boxName, pageNumber)
2021-11-10 17:14:51 +00:00
if postIsSensitive and reactionStr:
reactionStr = '<br>' + reactionStr
postHtml = ' <div id="' + timelinePostBookmark + \
'" class="' + containerClass + '">\n'
2020-11-09 19:42:09 +00:00
postHtml += avatarImageInPost
postHtml += ' <div class="post-title">\n' + \
' ' + titleStr + \
replyAvatarImageInPost + ' </div>\n'
2021-11-10 17:14:51 +00:00
postHtml += contentStr + citationsStr + reactionStr + footerStr + '\n'
2020-11-09 19:42:09 +00:00
postHtml += ' </div>\n'
else:
postHtml = galleryStr
2021-12-29 21:55:09 +00:00
_log_post_timing(enableTimingLog, postStartTime, '18')
2020-11-09 19:42:09 +00:00
2020-11-30 20:52:58 +00:00
# save the created html to the recent posts cache
2020-11-09 19:42:09 +00:00
if not showPublicOnly and storeToCache and \
boxName != 'tlmedia' and boxName != 'tlbookmarks' and \
boxName != 'bookmarks':
2021-12-29 21:55:09 +00:00
_save_individual_post_as_html_to_cache(base_dir, nickname, domain,
post_json_object, postHtml)
2021-12-28 14:24:14 +00:00
update_recent_posts_cache(recent_posts_cache, max_recent_posts,
post_json_object, postHtml)
2020-11-09 19:42:09 +00:00
2021-12-29 21:55:09 +00:00
_log_post_timing(enableTimingLog, postStartTime, '19')
2020-11-09 19:42:09 +00:00
return postHtml
2020-11-09 22:44:03 +00:00
2021-12-29 21:55:09 +00:00
def html_individual_post(css_cache: {},
recent_posts_cache: {}, max_recent_posts: int,
translate: {},
base_dir: str, session, cached_webfingers: {},
person_cache: {},
nickname: str, domain: str, port: int,
authorized: bool,
post_json_object: {}, http_prefix: str,
project_version: str, likedBy: str,
reactBy: str, reactEmoji: str,
yt_replace_domain: str,
twitter_replacement_domain: str,
show_published_date_only: bool,
peertube_instances: [],
allow_local_network_access: bool,
theme_name: str, system_language: str,
max_like_count: int, signing_priv_key_pem: str,
cw_lists: {}, lists_enabled: str) -> str:
2020-11-09 22:44:03 +00:00
"""Show an individual post as html
"""
2021-12-25 22:09:19 +00:00
originalPostJson = post_json_object
2020-11-09 22:44:03 +00:00
postStr = ''
2021-11-10 19:13:23 +00:00
byStr = ''
byText = ''
byTextExtra = ''
2020-11-09 22:44:03 +00:00
if likedBy:
2021-11-10 19:13:23 +00:00
byStr = likedBy
byText = 'Liked by'
elif reactBy and reactEmoji:
byStr = reactBy
byText = 'Reaction by'
byTextExtra = ' ' + reactEmoji
if byStr:
2021-12-27 22:19:18 +00:00
byStrNickname = get_nickname_from_actor(byStr)
2021-12-27 19:05:25 +00:00
byStrDomain, byStrPort = get_domain_from_actor(byStr)
2021-12-26 12:45:03 +00:00
byStrDomain = get_full_domain(byStrDomain, byStrPort)
2021-11-10 19:13:23 +00:00
byStrHandle = byStrNickname + '@' + byStrDomain
if translate.get(byText):
byText = translate[byText]
2020-11-09 22:44:03 +00:00
postStr += \
2021-11-10 19:13:23 +00:00
'<p>' + byText + ' <a href="' + byStr + '">@' + \
byStrHandle + '</a>' + byTextExtra + '\n'
2020-11-09 22:44:03 +00:00
2021-12-26 12:45:03 +00:00
domain_full = get_full_domain(domain, port)
2020-11-09 22:44:03 +00:00
actor = '/users/' + nickname
followStr = ' <form method="POST" ' + \
'accept-charset="UTF-8" action="' + actor + '/searchhandle">\n'
followStr += \
' <input type="hidden" name="actor" value="' + actor + '">\n'
followStr += \
' <input type="hidden" name="searchtext" value="' + \
2021-11-10 19:13:23 +00:00
byStrHandle + '">\n'
2021-12-28 20:32:11 +00:00
if not is_following_actor(base_dir, nickname, domain_full, byStr):
translateFollowStr = 'Follow'
if translate.get(translateFollowStr):
translateFollowStr = translate[translateFollowStr]
2020-11-09 22:44:03 +00:00
followStr += ' <button type="submit" class="button" ' + \
'name="submitSearch">' + translateFollowStr + '</button>\n'
goBackStr = 'Go Back'
if translate.get(goBackStr):
goBackStr = translate[goBackStr]
2020-11-09 22:44:03 +00:00
followStr += ' <button type="submit" class="button" ' + \
'name="submitBack">' + goBackStr + '</button>\n'
2020-11-09 22:44:03 +00:00
followStr += ' </form>\n'
postStr += followStr + '</p>\n'
postStr += \
2021-12-29 21:55:09 +00:00
individual_post_as_html(signing_priv_key_pem,
True, recent_posts_cache, max_recent_posts,
translate, None,
base_dir, session,
cached_webfingers, person_cache,
nickname, domain, port, post_json_object,
None, True, False,
http_prefix, project_version, 'inbox',
yt_replace_domain,
twitter_replacement_domain,
show_published_date_only,
peertube_instances,
allow_local_network_access, theme_name,
system_language, max_like_count,
False, authorized, False, False, False, False,
cw_lists, lists_enabled)
2021-12-27 11:20:57 +00:00
messageId = remove_id_ending(post_json_object['id'])
2020-11-09 22:44:03 +00:00
# show the previous posts
2021-12-26 10:57:03 +00:00
if has_object_dict(post_json_object):
2021-12-25 22:09:19 +00:00
while post_json_object['object'].get('inReplyTo'):
2021-12-26 23:41:34 +00:00
post_filename = \
2021-12-26 20:36:08 +00:00
locate_post(base_dir, nickname, domain,
post_json_object['object']['inReplyTo'])
2021-12-26 23:41:34 +00:00
if not post_filename:
2020-11-09 22:44:03 +00:00
break
2021-12-26 23:41:34 +00:00
post_json_object = load_json(post_filename)
2021-12-25 22:09:19 +00:00
if post_json_object:
2020-11-09 22:44:03 +00:00
postStr = \
2021-12-29 21:55:09 +00:00
individual_post_as_html(signing_priv_key_pem,
True, recent_posts_cache,
max_recent_posts,
translate, None,
base_dir, session,
cached_webfingers,
person_cache,
nickname, domain, port,
post_json_object,
None, True, False,
http_prefix, project_version,
'inbox',
yt_replace_domain,
twitter_replacement_domain,
show_published_date_only,
peertube_instances,
allow_local_network_access,
theme_name, system_language,
max_like_count,
False, authorized,
False, False, False, False,
cw_lists, lists_enabled) + postStr
2020-11-09 22:44:03 +00:00
# show the following posts
2021-12-26 23:41:34 +00:00
post_filename = locate_post(base_dir, nickname, domain, messageId)
if post_filename:
2020-11-09 22:44:03 +00:00
# is there a replies file for this post?
2021-12-26 23:41:34 +00:00
repliesFilename = post_filename.replace('.json', '.replies')
2020-11-09 22:44:03 +00:00
if os.path.isfile(repliesFilename):
# get items from the replies file
repliesJson = {
'orderedItems': []
}
2021-12-28 19:33:29 +00:00
populate_replies_json(base_dir, nickname, domain,
repliesFilename, authorized, repliesJson)
2020-11-09 22:44:03 +00:00
# add items to the html output
for item in repliesJson['orderedItems']:
postStr += \
2021-12-29 21:55:09 +00:00
individual_post_as_html(signing_priv_key_pem,
True, recent_posts_cache,
max_recent_posts,
translate, None,
base_dir, session,
cached_webfingers,
person_cache,
nickname, domain, port, item,
None, True, False,
http_prefix, project_version,
'inbox',
yt_replace_domain,
twitter_replacement_domain,
show_published_date_only,
peertube_instances,
allow_local_network_access,
theme_name, system_language,
max_like_count,
False, authorized,
False, False, False, False,
cw_lists, lists_enabled)
2021-12-25 16:17:53 +00:00
cssFilename = base_dir + '/epicyon-profile.css'
if os.path.isfile(base_dir + '/epicyon.css'):
cssFilename = base_dir + '/epicyon.css'
2020-11-09 22:44:03 +00:00
2021-01-11 19:46:21 +00:00
instanceTitle = \
2021-12-26 14:08:58 +00:00
get_config_param(base_dir, 'instanceTitle')
2021-12-29 21:55:09 +00:00
metadataStr = _html_post_metadata_open_graph(domain, originalPostJson)
headerStr = html_header_with_external_style(cssFilename,
instanceTitle, metadataStr)
return headerStr + postStr + html_footer()
def html_post_replies(css_cache: {},
recent_posts_cache: {}, max_recent_posts: int,
translate: {}, base_dir: str,
session, cached_webfingers: {}, person_cache: {},
nickname: str, domain: str, port: int, repliesJson: {},
http_prefix: str, project_version: str,
yt_replace_domain: str,
twitter_replacement_domain: str,
show_published_date_only: bool,
peertube_instances: [],
allow_local_network_access: bool,
theme_name: str, system_language: str,
max_like_count: int,
signing_priv_key_pem: str, cw_lists: {},
lists_enabled: str) -> str:
2020-11-09 22:44:03 +00:00
"""Show the replies to an individual post as html
"""
repliesStr = ''
if repliesJson.get('orderedItems'):
for item in repliesJson['orderedItems']:
repliesStr += \
2021-12-29 21:55:09 +00:00
individual_post_as_html(signing_priv_key_pem,
True, recent_posts_cache,
max_recent_posts,
translate, None,
base_dir, session, cached_webfingers,
person_cache,
nickname, domain, port, item,
None, True, False,
http_prefix, project_version, 'inbox',
yt_replace_domain,
twitter_replacement_domain,
show_published_date_only,
peertube_instances,
allow_local_network_access,
theme_name, system_language,
max_like_count,
False, False, False, False,
False, False,
cw_lists, lists_enabled)
2020-11-09 22:44:03 +00:00
2021-12-25 16:17:53 +00:00
cssFilename = base_dir + '/epicyon-profile.css'
if os.path.isfile(base_dir + '/epicyon.css'):
cssFilename = base_dir + '/epicyon.css'
2020-11-09 22:44:03 +00:00
2021-12-26 14:08:58 +00:00
instanceTitle = get_config_param(base_dir, 'instanceTitle')
metadata = ''
headerStr = \
2021-12-29 21:55:09 +00:00
html_header_with_external_style(cssFilename, instanceTitle, metadata)
return headerStr + repliesStr + html_footer()
def html_emoji_reaction_picker(css_cache: {},
recent_posts_cache: {}, max_recent_posts: int,
translate: {},
base_dir: str, session, cached_webfingers: {},
person_cache: {},
nickname: str, domain: str, port: int,
post_json_object: {}, http_prefix: str,
project_version: str,
yt_replace_domain: str,
twitter_replacement_domain: str,
show_published_date_only: bool,
peertube_instances: [],
allow_local_network_access: bool,
theme_name: str, system_language: str,
max_like_count: int, signing_priv_key_pem: str,
cw_lists: {}, lists_enabled: str,
boxName: str, pageNumber: int) -> str:
2021-11-11 22:11:12 +00:00
"""Returns the emoji picker screen
"""
reactedToPostStr = \
2021-11-11 23:26:08 +00:00
'<br><center><label class="followText">' + \
2021-11-11 23:18:57 +00:00
translate['Select reaction'].title() + '</label></center>\n' + \
2021-12-29 21:55:09 +00:00
individual_post_as_html(signing_priv_key_pem,
True, recent_posts_cache,
max_recent_posts,
translate, None,
base_dir, session, cached_webfingers,
person_cache,
nickname, domain, port, post_json_object,
None, True, False,
http_prefix, project_version, 'inbox',
yt_replace_domain,
twitter_replacement_domain,
show_published_date_only,
peertube_instances,
allow_local_network_access,
theme_name, system_language,
max_like_count,
False, False, False, False, False, False,
cw_lists, lists_enabled)
2021-11-11 22:11:12 +00:00
2021-12-25 16:17:53 +00:00
reactionsFilename = base_dir + '/emoji/reactions.json'
2021-11-11 22:11:12 +00:00
if not os.path.isfile(reactionsFilename):
2021-12-25 16:17:53 +00:00
reactionsFilename = base_dir + '/emoji/default_reactions.json'
2021-12-26 15:13:34 +00:00
reactionsJson = load_json(reactionsFilename)
2021-11-11 23:18:57 +00:00
emojiPicksStr = ''
2021-11-11 22:11:12 +00:00
baseUrl = '/users/' + nickname
2021-12-27 11:20:57 +00:00
post_id = remove_id_ending(post_json_object['id'])
2021-11-11 22:11:12 +00:00
for category, item in reactionsJson.items():
2021-11-11 22:41:12 +00:00
emojiPicksStr += '<div class="container">\n'
2021-11-11 22:11:12 +00:00
for emojiContent in item:
emojiContentEncoded = urllib.parse.quote_plus(emojiContent)
emojiUrl = \
2021-12-26 19:47:06 +00:00
baseUrl + '?react=' + post_id + \
2021-12-25 22:09:19 +00:00
'?actor=' + post_json_object['actor'] + \
2021-11-12 11:54:47 +00:00
'?tl=' + boxName + \
'?page=' + str(pageNumber) + \
2021-11-11 22:11:12 +00:00
'?emojreact=' + emojiContentEncoded
emojiLabel = '<label class="rlab">' + emojiContent + '</label>'
emojiPicksStr += \
' <a href="' + emojiUrl + '">' + emojiLabel + '</a>\n'
emojiPicksStr += '</div>\n'
2021-11-11 22:11:12 +00:00
2021-12-25 16:17:53 +00:00
cssFilename = base_dir + '/epicyon-profile.css'
if os.path.isfile(base_dir + '/epicyon.css'):
cssFilename = base_dir + '/epicyon.css'
2021-11-11 22:11:12 +00:00
# filename of the banner shown at the top
bannerFile, _ = \
2021-12-29 21:55:09 +00:00
get_banner_file(base_dir, nickname, domain, theme_name)
2021-12-26 14:08:58 +00:00
instanceTitle = get_config_param(base_dir, 'instanceTitle')
2021-11-11 22:11:12 +00:00
metadata = ''
headerStr = \
2021-12-29 21:55:09 +00:00
html_header_with_external_style(cssFilename, instanceTitle, metadata)
# banner
headerStr += \
'<header>\n' + \
2021-11-12 11:54:47 +00:00
'<a href="/users/' + nickname + '/' + boxName + \
'?page=' + str(pageNumber) + '" title="' + \
translate['Switch to timeline view'] + '" alt="' + \
translate['Switch to timeline view'] + '">\n'
headerStr += '<img loading="lazy" class="timeline-banner" ' + \
'alt="" ' + \
'src="/users/' + nickname + '/' + bannerFile + '" /></a>\n' + \
'</header>\n'
2021-12-29 21:55:09 +00:00
return headerStr + reactedToPostStr + emojiPicksStr + html_footer()