epicyon/webapp_post.py

3031 lines
127 KiB
Python
Raw Normal View History

2020-11-09 19:42:09 +00:00
__filename__ = "webapp_post.py"
__author__ = "Bob Mottram"
__license__ = "AGPL3+"
2022-02-03 13:58:20 +00:00
__version__ = "1.3.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
2022-03-02 13:49:51 +00:00
from announce import announced_by_person
from announce import no_of_announces
2021-12-29 21:55:09 +00:00
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
2022-12-27 21:30:20 +00:00
from utils import license_link_from_name
2022-11-11 11:26:17 +00:00
from utils import dont_speak_hashtags
2022-06-21 11:58:50 +00:00
from utils import remove_eol
from utils import disallow_announce
from utils import disallow_reply
2022-02-25 19:12:40 +00:00
from utils import convert_published_to_local_timezone
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
from utils import get_language_from_post
2022-01-28 10:07:35 +00:00
from utils import get_summary_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
2022-12-09 10:18:26 +00:00
from utils import is_reminder
from utils import is_chat_message
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
from utils import display_name_is_emoji
2021-12-28 14:41:10 +00:00
from utils import is_public_post
2022-11-15 19:22:00 +00:00
from utils import is_followers_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
2022-03-12 14:09:36 +00:00
from utils import is_unlisted_post
2022-07-05 16:21:48 +00:00
from content import detect_dogwhistles
2022-04-10 22:50:44 +00:00
from content import create_edits_html
2022-03-24 13:14:41 +00:00
from content import bold_reading_string
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 webapp_utils import language_right_to_left
2021-12-29 21:55:09 +00:00
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
from maps import html_open_street_map
2022-05-22 12:37:57 +00:00
from maps import set_map_preferences_coords
from maps import set_map_preferences_url
from maps import geocoords_from_map_link
2022-08-22 10:42:39 +00:00
from maps import get_location_from_tags
2021-12-29 21:55:09 +00:00
2022-04-13 16:32:17 +00:00
def _html_post_metadata_open_graph(domain: str, post_json_object: {},
system_language: str) -> str:
"""Returns html OpenGraph metadata for a post
"""
metadata = \
" <link rel=\"schema.DC\" " + \
"href=\"http://purl.org/dc/elements/1.1/\" />\n"
metadata += \
" <link rel=\"schema.DCTERMS\" " + \
"href=\"http://purl.org/dc/terms/\" />\n"
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"
2022-01-04 12:27:31 +00:00
obj_json = post_json_object
2021-12-26 10:57:03 +00:00
if has_object_dict(post_json_object):
2022-01-04 12:27:31 +00:00
obj_json = post_json_object['object']
if obj_json.get('id'):
metadata += " <meta name=\"DC.identifier\" " + \
2022-11-20 13:37:55 +00:00
"scheme=\"DCTERMS.URI\" content=\"" + obj_json['id'] + "\">\n"
if obj_json.get('summary'):
metadata += " <meta name=\"DC.title\" " + \
2022-11-20 13:37:55 +00:00
"content=\"" + obj_json['summary'] + "\">\n"
2022-01-04 12:27:31 +00:00
if obj_json.get('attributedTo'):
if isinstance(obj_json['attributedTo'], str):
attrib = obj_json['attributedTo']
actor_nick = get_nickname_from_actor(attrib)
if actor_nick:
actor_domain, _ = get_domain_from_actor(attrib)
actor_handle = actor_nick + '@' + actor_domain
metadata += \
" <meta name=\"DC.creator\" " + \
"scheme=\"DCTERMS.URI\" content=\"" + \
attrib + "\">\n"
metadata += \
" <meta content=\"@" + actor_handle + \
"\" property=\"og:title\" />\n"
2022-01-04 12:27:31 +00:00
if obj_json.get('url'):
metadata += \
2022-01-04 12:27:31 +00:00
" <meta content=\"" + obj_json['url'] + \
"\" property=\"og:url\" />\n"
2022-01-04 12:27:31 +00:00
if obj_json.get('published'):
metadata += " <meta name=\"DC.date\" " + \
"scheme=\"DCTERMS.W3CDTF\" content=\"" + \
obj_json['published'] + "\">\n"
metadata += \
2022-01-04 12:27:31 +00:00
" <meta content=\"" + obj_json['published'] + \
"\" property=\"og:published_time\" />\n"
2022-01-04 12:27:31 +00:00
if not obj_json.get('attachment') or obj_json.get('sensitive'):
if obj_json.get('content') and not obj_json.get('sensitive'):
2022-04-13 16:32:17 +00:00
obj_content = obj_json['content']
if obj_json.get('contentMap'):
if obj_json['contentMap'].get(system_language):
obj_content = obj_json['contentMap'][system_language]
description = remove_html(obj_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
2022-01-04 12:27:31 +00:00
for attach_json in obj_json['attachment']:
if not isinstance(attach_json, dict):
continue
2022-01-04 12:27:31 +00:00
if not attach_json.get('mediaType'):
continue
2022-01-04 12:27:31 +00:00
if not attach_json.get('url'):
2021-11-07 10:48:00 +00:00
continue
2022-01-04 12:27:31 +00:00
if not attach_json.get('name'):
2021-11-07 10:48:00 +00:00
continue
description = None
2022-01-04 12:27:31 +00:00
if attach_json['mediaType'].startswith('image/'):
description = 'Attached: 1 image'
2022-01-04 12:27:31 +00:00
elif attach_json['mediaType'].startswith('video/'):
description = 'Attached: 1 video'
2022-01-04 12:27:31 +00:00
elif attach_json['mediaType'].startswith('audio/'):
description = 'Attached: 1 audio'
2021-11-07 10:48:00 +00:00
if description:
2022-01-04 12:27:31 +00:00
if obj_json.get('content') and not obj_json.get('sensitive'):
2022-04-13 16:32:17 +00:00
obj_content = obj_json['content']
if obj_json.get('contentMap'):
if obj_json['contentMap'].get(system_language):
obj_content = obj_json['contentMap'][system_language]
description += '\n\n' + remove_html(obj_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 += \
2022-01-04 12:27:31 +00:00
" <meta content=\"" + attach_json['url'] + \
"\" property=\"og:image\" />\n"
metadata += \
2022-01-04 12:27:31 +00:00
" <meta content=\"" + attach_json['mediaType'] + \
"\" property=\"og:image:type\" />\n"
2022-01-04 12:27:31 +00:00
if attach_json.get('width'):
metadata += \
2022-01-04 12:27:31 +00:00
" <meta content=\"" + str(attach_json['width']) + \
"\" property=\"og:image:width\" />\n"
2022-01-04 12:27:31 +00:00
if attach_json.get('height'):
metadata += \
2022-01-04 12:27:31 +00:00
" <meta content=\"" + str(attach_json['height']) + \
"\" property=\"og:image:height\" />\n"
metadata += \
2022-01-04 12:27:31 +00:00
" <meta content=\"" + attach_json['name'] + \
"\" property=\"og:image:alt\" />\n"
2022-01-04 12:27:31 +00:00
if attach_json['mediaType'].startswith('image/'):
metadata += \
2021-11-07 10:54:06 +00:00
" <meta content=\"summary_large_image\" " + \
"property=\"twitter:card\" />\n"
return metadata
2022-01-04 12:27:31 +00:00
def _log_post_timing(enable_timing_log: bool, post_start_time,
2022-05-30 18:33:51 +00:00
debug_id: str) -> None:
2020-12-01 17:23:34 +00:00
"""Create a log of timings for performance tuning
"""
2022-01-04 12:27:31 +00:00
if not enable_timing_log:
2020-12-01 17:23:34 +00:00
return
2022-01-04 12:27:31 +00:00
time_diff = int((time.time() - post_start_time) * 1000)
2021-12-31 21:18:12 +00:00
if time_diff > 100:
2022-05-30 18:33:51 +00:00
print('TIMING INDIV ' + debug_id + ' = ' + str(time_diff))
2020-12-01 17:23:34 +00:00
2022-01-04 12:27:31 +00:00
def prepare_html_post_nickname(nickname: str, post_html: 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
2022-01-04 12:27:31 +00:00
users_str = ' href="/users/'
if users_str not in post_html:
return post_html
user_found = True
post_str = post_html
new_post_str = ''
while user_found:
if users_str not in post_str:
new_post_str += post_str
2021-02-02 21:08:33 +00:00
break
# the next part, after href="/users/nickname?
2022-01-04 12:27:31 +00:00
next_str = post_str.split(users_str, 1)[1]
if '?' in next_str:
next_str = next_str.split('?', 1)[1]
2021-02-02 21:08:33 +00:00
else:
2022-01-04 12:27:31 +00:00
new_post_str += post_str
2021-02-02 21:08:33 +00:00
break
# append the previous text to the result
2022-01-04 12:27:31 +00:00
new_post_str += post_str.split(users_str)[0]
new_post_str += users_str + nickname + '?'
2021-02-02 21:08:33 +00:00
# post is now the next part
2022-01-04 12:27:31 +00:00
post_str = next_str
return new_post_str
2021-02-02 21:08:33 +00:00
def replace_link_variable(link: str, variable_name: str, value: str,
separator: str = '?') -> str:
"""Replaces a variable within the given link
"""
full_var = separator + variable_name + '='
if full_var not in link:
return link
curr_str = link
result = ''
while full_var in curr_str:
prefix = curr_str.split(full_var, 1)[0] + full_var
next_str = curr_str.split(full_var, 1)[1]
if separator in next_str:
next_str = next_str.split(separator, 1)[1]
result += prefix + value + separator
curr_str = next_str
else:
result += prefix + value
curr_str = ''
return result + curr_str
2022-01-04 12:27:31 +00:00
def prepare_post_from_html_cache(nickname: str, post_html: str, box_name: str,
page_number: int, first_post_id: str) -> 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
2022-01-04 12:27:31 +00:00
if box_name in ('tlbookmarks', 'bookmarks'):
post_html = post_html.replace('?tl=inbox', '?tl=tlbookmarks')
if '?page=' in post_html:
page_number_str = post_html.split('?page=')[1]
if '?' in page_number_str:
page_number_str = page_number_str.split('?')[0]
post_html = \
post_html.replace('?page=' + page_number_str, '?page=-999')
# add the page number
2022-01-04 12:27:31 +00:00
with_page_number = \
post_html.replace(';-999;', ';' + str(page_number) + ';')
with_page_number = \
with_page_number.replace('?page=-999', '?page=' + str(page_number))
# add first post in the timeline
if first_post_id is None:
first_post_id = ''
2022-12-28 19:02:04 +00:00
first_post_id = first_post_id.replace('#', '/')
if '?firstpost=' in with_page_number:
with_page_number = \
replace_link_variable(with_page_number,
'firstpost', first_post_id, '?')
elif ';firstpost=' in with_page_number:
with_page_number = \
replace_link_variable(with_page_number,
'firstpost', first_post_id, ';')
else:
with_page_number = \
with_page_number.replace('?page=',
'?firstpost=' + first_post_id +
'?page=')
2022-01-04 12:27:31 +00:00
return prepare_html_post_nickname(nickname, with_page_number)
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: {},
2022-01-04 12:27:31 +00:00
post_html: 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
"""
2022-01-04 12:27:31 +00:00
html_post_cache_dir = \
2021-12-26 23:53:16 +00:00
get_cached_post_directory(base_dir, nickname, domain)
2022-01-04 12:27:31 +00:00
cached_post_filename = \
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
2022-01-04 12:27:31 +00:00
if not os.path.isdir(html_post_cache_dir):
os.mkdir(html_post_cache_dir)
2020-11-09 19:42:09 +00:00
try:
2022-06-09 14:46:30 +00:00
with open(cached_post_filename, 'w+', encoding='utf-8') as fp_cache:
2022-01-04 12:27:31 +00:00
fp_cache.write(post_html)
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: {},
2022-01-04 12:27:31 +00:00
post_actor: str,
2021-12-29 21:55:09 +00:00
person_cache: {},
2022-01-04 12:27:31 +00:00
allow_downloads: bool,
show_public_only: bool,
store_to_cache: bool,
box_name: str,
avatar_url: str,
enable_timing_log: bool,
post_start_time,
page_number: int,
2021-12-29 21:55:09 +00:00
recent_posts_cache: {},
max_recent_posts: int,
signing_priv_key_pem: str,
first_post_id: str) -> str:
2020-11-30 12:47:52 +00:00
"""Attempts to get the html post from the recent posts cache in memory
"""
2022-01-04 12:27:31 +00:00
if box_name == 'tlmedia':
2020-11-30 12:47:52 +00:00
return None
2022-01-04 12:27:31 +00:00
if show_public_only:
2020-11-30 12:47:52 +00:00
return None
2022-01-04 12:27:31 +00:00
try_cache = False
bm_timeline = box_name in ('bookmarks', 'tlbookmarks')
if store_to_cache or bm_timeline:
try_cache = True
2020-11-30 12:47:52 +00:00
2022-01-04 12:27:31 +00:00
if not try_cache:
2020-11-30 12:47:52 +00:00
return None
# update avatar if needed
2022-01-04 12:27:31 +00:00
if not avatar_url:
avatar_url = \
2022-06-12 12:30:14 +00:00
get_person_avatar_url(base_dir, post_actor, person_cache)
2020-11-30 12:47:52 +00:00
2022-01-04 12:27:31 +00:00
_log_post_timing(enable_timing_log, post_start_time, '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,
2022-01-04 12:27:31 +00:00
post_actor, avatar_url, person_cache,
allow_downloads)
2020-11-30 12:47:52 +00:00
2022-01-04 12:27:31 +00:00
_log_post_timing(enable_timing_log, post_start_time, '2.2')
2020-11-30 12:47:52 +00:00
2022-01-04 12:27:31 +00:00
post_html = \
2021-12-29 21:55:09 +00:00
load_individual_post_as_html_from_cache(base_dir, nickname, domain,
post_json_object)
2022-01-04 12:27:31 +00:00
if not post_html:
2020-11-30 12:47:52 +00:00
return None
2022-01-04 12:27:31 +00:00
post_html = \
prepare_post_from_html_cache(nickname, post_html,
box_name, page_number, first_post_id)
2021-12-28 14:24:14 +00:00
update_recent_posts_cache(recent_posts_cache, max_recent_posts,
2022-01-04 12:27:31 +00:00
post_json_object, post_html)
_log_post_timing(enable_timing_log, post_start_time, '3')
return post_html
2020-11-30 12:47:52 +00:00
2022-05-30 18:33:51 +00:00
def _get_avatar_image_html(show_avatar_options: bool,
2021-12-29 21:55:09 +00:00
nickname: str, domain_full: str,
2022-01-04 12:27:31 +00:00
avatar_url: str, post_actor: str,
translate: {}, avatar_position: str,
page_number: int, message_id_str: str) -> str:
2020-11-30 15:20:10 +00:00
"""Get html for the avatar image
"""
# don't use svg images
if avatar_url.endswith('.svg'):
2022-05-26 16:11:46 +00:00
avatar_url = '/icons/avatar_default.png'
2022-01-04 12:27:31 +00:00
avatar_link = ''
if '/users/news/' not in avatar_url:
avatar_link = \
2022-05-25 11:45:54 +00:00
' <a class="imageAnchor" href="' + \
post_actor + '" tabindex="10">'
2022-01-04 12:27:31 +00:00
show_profile_str = 'Show profile'
if translate.get(show_profile_str):
show_profile_str = translate[show_profile_str]
avatar_link += \
2022-03-28 08:47:53 +00:00
'<img loading="lazy" decoding="async" ' + \
'src="' + avatar_url + '" title="' + \
2022-01-04 12:27:31 +00:00
show_profile_str + '" alt=" "' + avatar_position + \
2021-12-29 21:55:09 +00:00
get_broken_link_substitute() + '/></a>\n'
2020-11-30 15:20:10 +00:00
2022-05-30 18:33:51 +00:00
if show_avatar_options and \
2022-01-04 12:27:31 +00:00
domain_full + '/users/' + nickname not in post_actor:
show_options_for_this_person_str = 'Show options for this person'
if translate.get(show_options_for_this_person_str):
show_options_for_this_person_str = \
translate[show_options_for_this_person_str]
if '/users/news/' not in avatar_url:
avatar_link = \
2020-11-30 15:20:10 +00:00
' <a class="imageAnchor" href="/users/' + \
2022-01-04 12:27:31 +00:00
nickname + '?options=' + post_actor + \
';' + str(page_number) + ';' + \
2022-05-25 11:45:54 +00:00
avatar_url + message_id_str + '" tabindex="10">\n'
2022-01-04 12:27:31 +00:00
avatar_link += \
2022-03-28 08:47:53 +00:00
' <img loading="lazy" decoding="async" title="' + \
2022-01-04 12:27:31 +00:00
show_options_for_this_person_str + '" ' + \
2021-02-07 21:10:21 +00:00
'alt="👤 ' + \
2022-01-04 12:27:31 +00:00
show_options_for_this_person_str + '" ' + \
'src="' + avatar_url + '" ' + avatar_position + \
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
2022-01-04 12:27:31 +00:00
avatar_link += \
2022-03-28 08:47:53 +00:00
' <img loading="lazy" decoding="async" title="' + \
2022-01-04 12:27:31 +00:00
show_options_for_this_person_str + '" ' + \
2021-02-07 21:10:21 +00:00
'alt="👤 ' + \
2022-01-04 12:27:31 +00:00
show_options_for_this_person_str + '" ' + \
'src="' + avatar_url + '" ' + avatar_position + \
2021-12-29 21:55:09 +00:00
get_broken_link_substitute() + '/>\n'
2022-01-04 12:27:31 +00:00
return avatar_link.strip()
2020-11-30 15:20:10 +00:00
2021-12-29 21:55:09 +00:00
def _get_reply_icon_html(base_dir: str, nickname: str, domain: str,
2022-03-12 14:09:36 +00:00
is_public_reply: bool, is_unlisted_reply: bool,
2022-01-04 12:27:31 +00:00
show_icons: bool, comments_enabled: bool,
post_json_object: {}, page_number_param: str,
2021-12-29 21:55:09 +00:00
translate: {}, system_language: str,
2022-01-04 12:27:31 +00:00
conversation_id: str) -> str:
2020-11-30 16:00:36 +00:00
"""Returns html for the reply icon/button
"""
2022-01-04 12:27:31 +00:00
reply_str = ''
if not (show_icons and comments_enabled):
return reply_str
2020-12-01 09:51:55 +00:00
# reply is permitted - create reply icon
2022-01-04 12:27:31 +00:00
reply_to_link = remove_hash_from_post_id(post_json_object['object']['id'])
reply_to_link = remove_id_ending(reply_to_link)
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
2022-01-04 12:27:31 +00:00
block_nickname = \
2021-12-27 22:19:18 +00:00
get_nickname_from_actor(post_json_object['object']['replyTo'])
if not block_nickname:
return reply_str
2022-01-04 12:27:31 +00:00
block_domain, _ = \
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,
2022-01-04 12:27:31 +00:00
block_nickname, block_domain, {}):
reply_to_link = 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):
2022-01-04 12:27:31 +00:00
reply_to_link += \
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:
2022-01-04 12:27:31 +00:00
mentioned_actors = \
2021-12-30 20:24:05 +00:00
get_mentions_from_html(content,
"<span class=\"h-card\"><a href=\"")
2022-01-04 12:27:31 +00:00
if mentioned_actors:
for actor_url in mentioned_actors:
if '?mention=' + actor_url not in reply_to_link:
reply_to_link += '?mention=' + actor_url
if len(reply_to_link) > 500:
2020-12-01 09:51:55 +00:00
break
2022-01-04 12:27:31 +00:00
reply_to_link += page_number_param
reply_str = ''
reply_to_this_post_str = 'Reply to this post'
if translate.get(reply_to_this_post_str):
reply_to_this_post_str = translate[reply_to_this_post_str]
conversation_str = ''
if conversation_id:
conversation_str = '?conversationId=' + conversation_id
2022-03-12 14:09:36 +00:00
if is_public_reply:
2022-01-04 12:27:31 +00:00
reply_str += \
2020-12-01 09:51:55 +00:00
' <a class="imageAnchor" href="/users/' + \
2022-01-04 12:27:31 +00:00
nickname + '?replyto=' + reply_to_link + \
2021-12-25 22:09:19 +00:00
'?actor=' + post_json_object['actor'] + \
2022-01-04 12:27:31 +00:00
conversation_str + \
2022-05-25 11:45:54 +00:00
'" title="' + reply_to_this_post_str + '" tabindex="10">\n'
2022-03-12 14:09:36 +00:00
elif is_unlisted_reply:
reply_str += \
' <a class="imageAnchor" href="/users/' + \
nickname + '?replyunlisted=' + reply_to_link + \
'?actor=' + post_json_object['actor'] + \
conversation_str + \
2022-05-25 11:45:54 +00:00
'" title="' + reply_to_this_post_str + '" tabindex="10">\n'
2020-12-01 09:51:55 +00:00
else:
2021-12-26 20:12:18 +00:00
if is_dm(post_json_object):
reply_type = 'replydm'
if is_chat_message(post_json_object):
reply_type = 'replychat'
2022-01-04 12:27:31 +00:00
reply_str += \
2020-12-01 09:51:55 +00:00
' ' + \
'<a class="imageAnchor" href="/users/' + nickname + \
'?' + reply_type + '=' + reply_to_link + \
2021-12-25 22:09:19 +00:00
'?actor=' + post_json_object['actor'] + \
2022-01-04 12:27:31 +00:00
conversation_str + \
2022-05-25 11:45:54 +00:00
'" title="' + reply_to_this_post_str + '" tabindex="10">\n'
2020-11-30 16:00:36 +00:00
else:
2022-01-04 12:27:31 +00:00
reply_str += \
2020-12-01 09:51:55 +00:00
' ' + \
'<a class="imageAnchor" href="/users/' + nickname + \
2022-01-04 12:27:31 +00:00
'?replyfollowers=' + reply_to_link + \
2021-12-25 22:09:19 +00:00
'?actor=' + post_json_object['actor'] + \
2022-01-04 12:27:31 +00:00
conversation_str + \
2022-05-25 11:45:54 +00:00
'" title="' + reply_to_this_post_str + '" tabindex="10">\n'
2020-11-30 16:00:36 +00:00
2022-01-04 12:27:31 +00:00
reply_str += \
2020-12-01 09:51:55 +00:00
' ' + \
2022-03-28 08:47:53 +00:00
'<img loading="lazy" decoding="async" title="' + \
2022-01-04 12:27:31 +00:00
reply_to_this_post_str + '" alt="' + reply_to_this_post_str + \
2020-12-09 13:08:26 +00:00
' |" src="/icons/reply.png"/></a>\n'
2022-01-04 12:27:31 +00:00
return reply_str
2020-11-30 16:00:36 +00:00
2021-12-29 21:55:09 +00:00
def _get_edit_icon_html(base_dir: str, nickname: str, domain_full: str,
2022-01-04 12:27:31 +00:00
post_json_object: {}, actor_nickname: str,
translate: {}, is_event: bool,
first_post_id: str) -> str:
2020-11-30 16:48:30 +00:00
"""Returns html for the edit icon/button
"""
2022-01-04 12:27:31 +00:00
edit_str = ''
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:
2022-01-04 12:27:31 +00:00
return edit_str
2020-12-01 10:12:25 +00:00
2022-11-15 23:26:58 +00:00
reply_to = ''
if post_json_object['object'].get('inReplyTo'):
reply_to = ';replyTo=' + post_json_object['object']['inReplyTo']
first_post_str = ''
if first_post_id:
first_post_str = ';firstpost=' + first_post_id
2021-12-28 13:49:44 +00:00
if is_blog_post(post_json_object):
2022-01-04 12:27:31 +00:00
edit_blog_post_str = 'Edit blog post'
if translate.get(edit_blog_post_str):
edit_blog_post_str = translate[edit_blog_post_str]
2021-12-28 12:20:18 +00:00
if not is_news_post(post_json_object):
2022-01-04 12:27:31 +00:00
edit_str += \
2020-11-30 16:48:30 +00:00
' ' + \
2020-12-01 10:12:25 +00:00
'<a class="imageAnchor" href="/users/' + \
2022-11-15 20:02:50 +00:00
nickname + '/tlblogs?editblogpost=' + \
2021-12-26 19:47:06 +00:00
post_id.split('/statuses/')[1] + \
';actor=' + actor_nickname + first_post_str + \
2022-05-25 11:45:54 +00:00
'" title="' + edit_blog_post_str + '" tabindex="10">' + \
2022-03-28 08:47:53 +00:00
'<img loading="lazy" decoding="async" title="' + \
2022-01-04 12:27:31 +00:00
edit_blog_post_str + '" alt="' + edit_blog_post_str + \
2020-12-09 13:08:26 +00:00
' |" src="/icons/edit.png"/></a>\n'
2020-12-01 10:12:25 +00:00
else:
2022-01-04 12:27:31 +00:00
edit_str += \
2020-12-01 10:12:25 +00:00
' ' + \
'<a class="imageAnchor" href="/users/' + \
nickname + '/editnewspost=' + \
2021-12-26 19:47:06 +00:00
post_id.split('/statuses/')[1] + \
'?actor=' + actor_nickname + first_post_str + \
2022-05-25 11:45:54 +00:00
'" title="' + edit_blog_post_str + '" tabindex="10">' + \
2022-03-28 08:47:53 +00:00
'<img loading="lazy" decoding="async" title="' + \
2022-01-04 12:27:31 +00:00
edit_blog_post_str + '" alt="' + edit_blog_post_str + \
2020-12-09 13:08:26 +00:00
' |" src="/icons/edit.png"/></a>\n'
2022-05-30 18:33:51 +00:00
elif is_event:
2022-01-04 12:27:31 +00:00
edit_event_str = 'Edit event'
if translate.get(edit_event_str):
edit_event_str = translate[edit_event_str]
edit_str += \
2020-12-01 10:12:25 +00:00
' ' + \
'<a class="imageAnchor" href="/users/' + nickname + \
'/tlblogs?editeventpost=' + \
2021-12-26 19:47:06 +00:00
post_id.split('/statuses/')[1] + \
'?actor=' + actor_nickname + first_post_str + \
2022-05-25 11:45:54 +00:00
'" title="' + edit_event_str + '" tabindex="10">' + \
2022-03-28 08:47:53 +00:00
'<img loading="lazy" decoding="async" title="' + \
2022-01-04 12:27:31 +00:00
edit_event_str + '" alt="' + edit_event_str + \
2020-12-09 13:08:26 +00:00
' |" src="/icons/edit.png"/></a>\n'
2022-11-15 19:22:00 +00:00
elif is_public_post(post_json_object):
# Edit a public post
edit_post_str = 'Edit post'
if translate.get(edit_post_str):
edit_post_str = translate[edit_post_str]
edit_str += \
' ' + \
'<a class="imageAnchor" href="/users/' + \
2022-11-16 20:16:48 +00:00
nickname + '?postedit=' + \
post_id.split('/statuses/')[1] + ';scope=public' + \
';actor=' + actor_nickname + first_post_str + reply_to + \
2022-11-15 19:22:00 +00:00
'" title="' + edit_post_str + '" tabindex="10">' + \
'<img loading="lazy" decoding="async" title="' + \
edit_post_str + '" alt="' + edit_post_str + \
' |" src="/icons/edit.png"/></a>\n'
2022-12-09 10:18:26 +00:00
elif is_reminder(post_json_object):
# Edit a reminder
edit_post_str = 'Edit reminder'
if translate.get(edit_post_str):
edit_post_str = translate[edit_post_str]
edit_str += \
' ' + \
'<a class="imageAnchor" href="/users/' + \
nickname + '?postedit=' + \
post_id.split('/statuses/')[1] + ';scope=reminder' + \
';actor=' + actor_nickname + first_post_str + reply_to + \
2022-12-09 10:18:26 +00:00
'" title="' + edit_post_str + '" tabindex="10">' + \
'<img loading="lazy" decoding="async" title="' + \
edit_post_str + '" alt="' + edit_post_str + \
' |" src="/icons/edit.png"/></a>\n'
2022-11-15 19:22:00 +00:00
elif is_dm(post_json_object):
# Edit a DM
edit_post_str = 'Edit post'
if translate.get(edit_post_str):
edit_post_str = translate[edit_post_str]
edit_str += \
' ' + \
'<a class="imageAnchor" href="/users/' + \
2022-11-16 20:16:48 +00:00
nickname + '?postedit=' + \
post_id.split('/statuses/')[1] + ';scope=dm' + \
';actor=' + actor_nickname + first_post_str + reply_to + \
2022-11-15 19:22:00 +00:00
'" title="' + edit_post_str + '" tabindex="10">' + \
'<img loading="lazy" decoding="async" title="' + \
edit_post_str + '" alt="' + edit_post_str + \
' |" src="/icons/edit.png"/></a>\n'
elif is_unlisted_post(post_json_object):
# Edit an unlisted post
edit_post_str = 'Edit post'
if translate.get(edit_post_str):
edit_post_str = translate[edit_post_str]
edit_str += \
' ' + \
'<a class="imageAnchor" href="/users/' + \
2022-11-16 20:16:48 +00:00
nickname + '?postedit=' + \
post_id.split('/statuses/')[1] + ';scope=unlisted' + \
';actor=' + actor_nickname + first_post_str + reply_to + \
2022-11-15 19:22:00 +00:00
'" title="' + edit_post_str + '" tabindex="10">' + \
'<img loading="lazy" decoding="async" title="' + \
edit_post_str + '" alt="' + edit_post_str + \
' |" src="/icons/edit.png"/></a>\n'
elif is_followers_post(post_json_object):
# Edit a followers only post
edit_post_str = 'Edit post'
if translate.get(edit_post_str):
edit_post_str = translate[edit_post_str]
edit_str += \
' ' + \
'<a class="imageAnchor" href="/users/' + \
2022-11-16 20:16:48 +00:00
nickname + '?postedit=' + \
post_id.split('/statuses/')[1] + ';scope=followers' + \
';actor=' + actor_nickname + first_post_str + reply_to + \
2022-11-15 19:22:00 +00:00
'" title="' + edit_post_str + '" tabindex="10">' + \
'<img loading="lazy" decoding="async" title="' + \
edit_post_str + '" alt="' + edit_post_str + \
' |" src="/icons/edit.png"/></a>\n'
2022-01-04 12:27:31 +00:00
return edit_str
2020-11-30 16:48:30 +00:00
2022-01-04 12:27:31 +00:00
def _get_announce_icon_html(is_announced: bool,
post_actor: str,
2021-12-29 21:55:09 +00:00
nickname: str, domain_full: str,
2022-01-04 12:27:31 +00:00
announce_json_object: {},
2021-12-29 21:55:09 +00:00
post_json_object: {},
2022-01-04 12:27:31 +00:00
is_public_repeat: bool,
is_moderation_post: bool,
show_repeat_icon: bool,
2021-12-29 21:55:09 +00:00
translate: {},
2022-01-04 12:27:31 +00:00
page_number_param: str,
timeline_post_bookmark: str,
2022-03-02 13:49:51 +00:00
box_name: str,
max_announce_count: int,
first_post_id: str) -> str:
"""Returns html for announce icon/button at the bottom of the post
"""
2022-01-04 12:27:31 +00:00
announce_str = ''
2021-05-07 12:42:01 +00:00
2022-01-04 12:27:31 +00:00
if not show_repeat_icon:
return announce_str
2021-05-07 12:42:01 +00:00
2022-01-04 12:27:31 +00:00
if is_moderation_post:
return announce_str
2021-05-07 12:42:01 +00:00
# don't allow announce/repeat of your own posts
2022-01-04 12:27:31 +00:00
announce_icon = 'repeat_inactive.png'
announce_link = 'repeat'
announce_emoji = ''
if not is_public_repeat:
announce_link = 'repeatprivate'
repeat_this_post_str = 'Repeat this post'
if translate.get(repeat_this_post_str):
repeat_this_post_str = translate[repeat_this_post_str]
announce_title = repeat_this_post_str
unannounce_link_str = ''
2022-03-02 13:49:51 +00:00
announce_count = no_of_announces(post_json_object)
2022-01-04 12:27:31 +00:00
2022-03-02 13:49:51 +00:00
announce_count_str = ''
if announce_count > 0:
if announce_count <= max_announce_count:
announce_count_str = ' (' + str(announce_count) + ')'
else:
announce_count_str = ' (' + str(max_announce_count) + '+)'
2022-01-04 12:27:31 +00:00
if announced_by_person(is_announced,
post_actor, nickname, domain_full):
2022-03-02 13:49:51 +00:00
if announce_count == 1:
# announced by the reader only
announce_count_str = ''
2022-01-04 12:27:31 +00:00
announce_icon = 'repeat.png'
announce_emoji = '🔁 '
announce_link = 'unrepeat'
if not is_public_repeat:
announce_link = 'unrepeatprivate'
undo_the_repeat_str = 'Undo the repeat'
if translate.get(undo_the_repeat_str):
undo_the_repeat_str = translate[undo_the_repeat_str]
announce_title = undo_the_repeat_str
if announce_json_object:
unannounce_link_str = '?unannounce=' + \
remove_id_ending(announce_json_object['id'])
announce_post_id = \
remove_hash_from_post_id(post_json_object['object']['id'])
announce_post_id = remove_id_ending(announce_post_id)
2022-03-02 13:49:51 +00:00
announce_str = ''
if announce_count_str:
announcers_post_id = announce_post_id.replace('/', '--')
announcers_screen_link = \
'/users/' + nickname + '?announcers=' + announcers_post_id
# show the number of announces next to icon
announce_str += '<label class="likesCount">'
announce_str += '<a href="' + announcers_screen_link + '" ' + \
2022-05-25 11:45:54 +00:00
'title="' + translate['Show who repeated this post'] + \
'" tabindex="10">'
2022-03-02 13:49:51 +00:00
announce_str += \
announce_count_str.replace('(', '').replace(')', '').strip()
announce_str += '</a></label>\n'
first_post_str = ''
if first_post_id:
first_post_str = '?firstpost=' + first_post_id
2022-01-04 12:27:31 +00:00
announce_link_str = '?' + \
announce_link + '=' + announce_post_id + page_number_param
2022-03-02 13:49:51 +00:00
announce_str += \
2021-05-07 12:42:01 +00:00
' <a class="imageAnchor" href="/users/' + \
2022-01-04 12:27:31 +00:00
nickname + announce_link_str + unannounce_link_str + \
2021-12-25 22:09:19 +00:00
'?actor=' + post_json_object['actor'] + \
'?bm=' + timeline_post_bookmark + first_post_str + \
2022-05-25 11:45:54 +00:00
'?tl=' + box_name + '" title="' + announce_title + '" tabindex="10">\n'
2021-05-07 12:42:01 +00:00
2022-01-04 12:27:31 +00:00
announce_str += \
2021-05-07 12:42:01 +00:00
' ' + \
2022-03-28 08:47:53 +00:00
'<img loading="lazy" decoding="async" title="' + announce_title + \
2022-01-04 12:27:31 +00:00
'" alt="' + announce_emoji + announce_title + \
' |" src="/icons/' + announce_icon + '"/></a>\n'
return announce_str
2021-12-29 21:55:09 +00:00
def _get_like_icon_html(nickname: str, domain_full: str,
2022-01-04 12:27:31 +00:00
is_moderation_post: bool,
show_like_button: bool,
2021-12-29 21:55:09 +00:00
post_json_object: {},
2022-01-04 12:27:31 +00:00
enable_timing_log: bool,
post_start_time,
translate: {}, page_number_param: str,
timeline_post_bookmark: str,
box_name: str,
max_like_count: int,
first_post_id: str) -> str:
2020-11-30 17:06:55 +00:00
"""Returns html for like icon/button
"""
2022-01-04 12:27:31 +00:00
if not show_like_button or is_moderation_post:
2021-10-15 09:02:18 +00:00
return ''
2022-01-04 12:27:31 +00:00
like_str = ''
like_icon = 'like_inactive.png'
like_link = 'like'
like_title = 'Like this post'
if translate.get(like_title):
like_title = translate[like_title]
like_emoji = ''
like_count = no_of_likes(post_json_object)
_log_post_timing(enable_timing_log, post_start_time, '12.1')
like_count_str = ''
if like_count > 0:
if like_count <= max_like_count:
like_count_str = ' (' + str(like_count) + ')'
2021-10-15 09:02:18 +00:00
else:
2022-01-04 12:27:31 +00:00
like_count_str = ' (' + str(max_like_count) + '+)'
2021-12-29 21:55:09 +00:00
if liked_by_person(post_json_object, nickname, domain_full):
2022-01-04 12:27:31 +00:00
if like_count == 1:
2021-10-15 09:02:18 +00:00
# liked by the reader only
2022-01-04 12:27:31 +00:00
like_count_str = ''
like_icon = 'like.png'
like_link = 'unlike'
like_title = 'Undo the like'
if translate.get(like_title):
like_title = translate[like_title]
like_emoji = '👍 '
_log_post_timing(enable_timing_log, post_start_time, '12.2')
like_post_id = remove_hash_from_post_id(post_json_object['id'])
like_post_id = remove_id_ending(like_post_id)
2022-03-01 14:29:33 +00:00
like_str = ''
2022-03-01 14:14:08 +00:00
if like_count_str:
2022-03-01 14:29:33 +00:00
likers_post_id = like_post_id.replace('/', '--')
likers_screen_link = \
'/users/' + nickname + '?likers=' + likers_post_id
2022-03-01 14:29:33 +00:00
# show the number of likes next to icon
like_str += '<label class="likesCount">'
2022-03-01 17:48:13 +00:00
like_str += '<a href="' + likers_screen_link + '" ' + \
2022-05-25 11:45:54 +00:00
'title="' + translate['Show who liked this post'] + \
'" tabindex="10">'
2022-03-01 14:29:33 +00:00
like_str += like_count_str.replace('(', '').replace(')', '').strip()
like_str += '</a></label>\n'
first_post_str = ''
if first_post_id:
first_post_str = '?firstpost=' + first_post_id
2022-01-04 12:27:31 +00:00
like_str += \
2021-10-15 09:02:18 +00:00
' <a class="imageAnchor" href="/users/' + nickname + '?' + \
2022-01-04 12:27:31 +00:00
like_link + '=' + like_post_id + \
page_number_param + \
2021-12-25 22:09:19 +00:00
'?actor=' + post_json_object['actor'] + \
'?bm=' + timeline_post_bookmark + first_post_str + \
2022-05-25 11:45:54 +00:00
'?tl=' + box_name + '" title="' + like_title + like_count_str + \
'" tabindex="10">\n'
2022-01-04 12:27:31 +00:00
like_str += \
2021-10-15 09:02:18 +00:00
' ' + \
2022-03-28 08:47:53 +00:00
'<img loading="lazy" decoding="async" title="' + \
like_title + like_count_str + \
2022-01-04 12:27:31 +00:00
'" alt="' + like_emoji + like_title + \
' |" src="/icons/' + like_icon + '"/></a>\n'
return like_str
2020-11-30 17:06:55 +00:00
2021-12-29 21:55:09 +00:00
def _get_bookmark_icon_html(nickname: str, domain_full: str,
post_json_object: {},
2022-01-04 12:27:31 +00:00
is_moderation_post: bool,
2021-12-29 21:55:09 +00:00
translate: {},
2022-01-04 12:27:31 +00:00
enable_timing_log: bool,
post_start_time, box_name: str,
page_number_param: str,
timeline_post_bookmark: str,
first_post_id: str) -> str:
"""Returns html for bookmark icon/button
"""
2022-01-04 12:27:31 +00:00
bookmark_str = ''
2020-12-01 10:15:26 +00:00
2022-01-04 12:27:31 +00:00
if is_moderation_post:
return bookmark_str
2020-12-01 10:15:26 +00:00
2022-01-04 12:27:31 +00:00
bookmark_icon = 'bookmark_inactive.png'
bookmark_link = 'bookmark'
bookmark_emoji = ''
bookmark_title = 'Bookmark this post'
if translate.get(bookmark_title):
bookmark_title = translate[bookmark_title]
2021-12-29 21:55:09 +00:00
if bookmarked_by_person(post_json_object, nickname, domain_full):
2022-01-04 12:27:31 +00:00
bookmark_icon = 'bookmark.png'
bookmark_link = 'unbookmark'
bookmark_emoji = '🔖 '
bookmark_title = 'Undo the bookmark'
if translate.get(bookmark_title):
bookmark_title = translate[bookmark_title]
_log_post_timing(enable_timing_log, post_start_time, '12.6')
bookmark_post_id = \
remove_hash_from_post_id(post_json_object['object']['id'])
bookmark_post_id = remove_id_ending(bookmark_post_id)
first_post_str = ''
if first_post_id:
first_post_str = '?firstpost=' + first_post_id
2022-01-04 12:27:31 +00:00
bookmark_str = \
2020-12-01 10:15:26 +00:00
' <a class="imageAnchor" href="/users/' + nickname + '?' + \
2022-01-04 12:27:31 +00:00
bookmark_link + '=' + bookmark_post_id + \
page_number_param + \
2021-12-25 22:09:19 +00:00
'?actor=' + post_json_object['actor'] + \
'?bm=' + timeline_post_bookmark + first_post_str + \
2022-05-25 11:45:54 +00:00
'?tl=' + box_name + '" title="' + bookmark_title + \
'" tabindex="10">\n'
2022-01-04 12:27:31 +00:00
bookmark_str += \
2020-12-01 10:15:26 +00:00
' ' + \
2022-03-28 08:47:53 +00:00
'<img loading="lazy" decoding="async" title="' + \
bookmark_title + '" alt="' + \
2022-01-04 12:27:31 +00:00
bookmark_emoji + bookmark_title + ' |" src="/icons' + \
'/' + bookmark_icon + '"/></a>\n'
return bookmark_str
2022-06-01 17:45:59 +00:00
def _get_reaction_icon_html(nickname: str, post_json_object: {},
2022-01-04 12:27:31 +00:00
is_moderation_post: bool,
show_reaction_button: bool,
2021-12-29 21:55:09 +00:00
translate: {},
2022-01-04 12:27:31 +00:00
enable_timing_log: bool,
post_start_time, box_name: str,
page_number_param: str,
timeline_post_reaction: str,
first_post_id: str) -> str:
2021-11-11 15:02:03 +00:00
"""Returns html for reaction icon/button
"""
2022-01-04 12:27:31 +00:00
reaction_str = ''
2021-11-11 15:02:03 +00:00
2022-01-04 12:27:31 +00:00
if not show_reaction_button or is_moderation_post:
return reaction_str
2021-11-11 15:02:03 +00:00
2022-01-04 12:27:31 +00:00
reaction_icon = 'reaction.png'
reaction_title = 'Select reaction'
if translate.get(reaction_title):
reaction_title = translate[reaction_title]
_log_post_timing(enable_timing_log, post_start_time, '12.65')
reaction_post_id = \
2021-12-29 21:55:09 +00:00
remove_hash_from_post_id(post_json_object['object']['id'])
2022-01-04 12:27:31 +00:00
reaction_post_id = remove_id_ending(reaction_post_id)
first_post_str = ''
if first_post_id:
first_post_str = '?firstpost=' + first_post_id
2022-01-04 12:27:31 +00:00
reaction_str = \
2021-11-11 15:02:03 +00:00
' <a class="imageAnchor" href="/users/' + nickname + \
2022-01-04 12:27:31 +00:00
'?selreact=' + reaction_post_id + page_number_param + \
2021-12-25 22:09:19 +00:00
'?actor=' + post_json_object['actor'] + \
'?bm=' + timeline_post_reaction + first_post_str + \
2022-05-25 11:45:54 +00:00
'?tl=' + box_name + '" title="' + reaction_title + \
'" tabindex="10">\n'
2022-01-04 12:27:31 +00:00
reaction_str += \
2021-11-11 15:02:03 +00:00
' ' + \
2022-03-28 08:47:53 +00:00
'<img loading="lazy" decoding="async" title="' + \
reaction_title + '" alt="' + \
2022-01-04 12:27:31 +00:00
reaction_title + ' |" src="/icons' + \
'/' + reaction_icon + '"/></a>\n'
return reaction_str
2021-11-11 15:02:03 +00:00
2021-12-29 21:55:09 +00:00
def _get_mute_icon_html(is_muted: bool,
2022-01-04 12:27:31 +00:00
post_actor: str,
message_id: str,
2021-12-29 21:55:09 +00:00
nickname: str, domain_full: str,
allow_deletion: bool,
2022-01-04 12:27:31 +00:00
page_number_param: str,
box_name: str,
timeline_post_bookmark: str,
translate: {},
first_post_id: str) -> str:
2020-11-30 17:48:35 +00:00
"""Returns html for mute icon/button
"""
2022-01-04 12:27:31 +00:00
mute_str = ''
2021-12-25 21:29:53 +00:00
if (allow_deletion or
2022-01-04 12:27:31 +00:00
('/' + domain_full + '/' in post_actor and
message_id.startswith(post_actor))):
return mute_str
2020-11-30 17:48:35 +00:00
first_post_str = ''
if first_post_id:
first_post_str = '?firstpost=' + first_post_id
2021-12-29 21:55:09 +00:00
if not is_muted:
2022-01-04 12:27:31 +00:00
mute_this_post_str = 'Mute this post'
if translate.get('Mute this post'):
2022-01-04 12:27:31 +00:00
mute_this_post_str = translate[mute_this_post_str]
mute_str = \
2020-11-30 17:48:35 +00:00
' <a class="imageAnchor" href="/users/' + nickname + \
2022-01-04 12:27:31 +00:00
'?mute=' + message_id + page_number_param + '?tl=' + box_name + \
'?bm=' + timeline_post_bookmark + first_post_str + \
2022-05-25 11:45:54 +00:00
'" title="' + mute_this_post_str + '" tabindex="10">\n'
2022-01-04 12:27:31 +00:00
mute_str += \
2020-11-30 17:48:35 +00:00
' ' + \
2022-03-28 08:47:53 +00:00
'<img loading="lazy" decoding="async" alt="' + \
2022-01-04 12:27:31 +00:00
mute_this_post_str + \
' |" title="' + mute_this_post_str + \
2020-12-09 13:08:26 +00:00
'" src="/icons/mute.png"/></a>\n'
2020-11-30 17:48:35 +00:00
else:
2022-01-04 12:27:31 +00:00
undo_mute_str = 'Undo mute'
if translate.get(undo_mute_str):
undo_mute_str = translate[undo_mute_str]
mute_str = \
2020-11-30 17:48:35 +00:00
' <a class="imageAnchor" href="/users/' + \
2022-01-04 12:27:31 +00:00
nickname + '?unmute=' + message_id + \
page_number_param + '?tl=' + box_name + '?bm=' + \
timeline_post_bookmark + first_post_str + \
'" title="' + undo_mute_str + \
2022-05-25 11:45:54 +00:00
'" tabindex="10">\n'
2022-01-04 12:27:31 +00:00
mute_str += \
2020-11-30 17:48:35 +00:00
' ' + \
2022-03-28 08:47:53 +00:00
'<img loading="lazy" decoding="async" ' + \
'alt="🔇 ' + undo_mute_str + \
2022-01-04 12:27:31 +00:00
' |" title="' + undo_mute_str + \
2020-12-09 13:08:26 +00:00
'" src="/icons/unmute.png"/></a>\n'
2022-01-04 12:27:31 +00:00
return mute_str
2020-11-30 17:48:35 +00:00
2021-12-29 21:55:09 +00:00
def _get_delete_icon_html(nickname: str, domain_full: str,
allow_deletion: bool,
2022-01-04 12:27:31 +00:00
post_actor: str,
message_id: str,
2021-12-29 21:55:09 +00:00
post_json_object: {},
2022-01-04 12:27:31 +00:00
page_number_param: str,
translate: {},
first_post_id: str) -> str:
"""Returns html for delete icon/button
"""
2022-01-04 12:27:31 +00:00
delete_str = ''
2021-12-25 21:29:53 +00:00
if (allow_deletion or
2022-01-04 12:27:31 +00:00
('/' + domain_full + '/' in post_actor and
message_id.startswith(post_actor))):
if '/users/' + nickname + '/' in message_id:
2021-12-28 12:20:18 +00:00
if not is_news_post(post_json_object):
2022-01-04 12:27:31 +00:00
delete_this_post_str = 'Delete this post'
if translate.get(delete_this_post_str):
delete_this_post_str = translate[delete_this_post_str]
first_post_str = ''
if first_post_id:
first_post_str = '?firstpost=' + first_post_id
2022-01-04 12:27:31 +00:00
delete_str = \
' <a class="imageAnchor" href="/users/' + \
nickname + '?delete=' + message_id + \
page_number_param + first_post_str + \
2022-05-25 11:45:54 +00:00
'" title="' + delete_this_post_str + '" tabindex="10">\n'
2022-01-04 12:27:31 +00:00
delete_str += \
' ' + \
2022-03-28 08:47:53 +00:00
'<img loading="lazy" decoding="async" alt="' + \
2022-01-04 12:27:31 +00:00
delete_this_post_str + \
' |" title="' + delete_this_post_str + \
2020-12-09 13:08:26 +00:00
'" src="/icons/delete.png"/></a>\n'
2022-01-04 12:27:31 +00:00
return delete_str
2021-12-29 21:55:09 +00:00
def _get_published_date_str(post_json_object: {},
2022-02-25 19:12:40 +00:00
show_published_date_only: bool,
timezone: str) -> str:
"""Return the html for the published date on a post
"""
2022-01-04 12:27:31 +00:00
published_str = ''
2020-12-01 10:19:53 +00:00
2021-12-25 22:09:19 +00:00
if not post_json_object['object'].get('published'):
2022-01-04 12:27:31 +00:00
return published_str
2020-12-01 10:19:53 +00:00
2022-01-04 12:27:31 +00:00
published_str = post_json_object['object']['published']
if '.' not in published_str:
if '+' not in published_str:
datetime_object = \
datetime.strptime(published_str, "%Y-%m-%dT%H:%M:%SZ")
else:
2022-01-04 12:27:31 +00:00
datetime_object = \
datetime.strptime(published_str.split('+')[0] + 'Z',
2020-12-01 10:19:53 +00:00
"%Y-%m-%dT%H:%M:%SZ")
else:
2022-01-04 12:27:31 +00:00
published_str = \
published_str.replace('T', ' ').split('.')[0]
datetime_object = parse(published_str)
2022-02-25 19:12:40 +00:00
# convert to local time
datetime_object = \
convert_published_to_local_timezone(datetime_object, timezone)
2021-12-25 20:06:27 +00:00
if not show_published_date_only:
2022-01-04 12:27:31 +00:00
published_str = datetime_object.strftime("%a %b %d, %H:%M")
2020-12-01 10:19:53 +00:00
else:
2022-01-04 12:27:31 +00:00
published_str = datetime_object.strftime("%a %b %d")
2020-12-01 10:19:53 +00:00
# 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:
2022-01-04 12:27:31 +00:00
published_str = '[' + published_str + ']'
return published_str
2022-01-04 12:27:31 +00:00
def _get_blog_citations_html(box_name: str,
2021-12-29 21:55:09 +00:00
post_json_object: {},
translate: {}) -> str:
2020-11-30 20:52:58 +00:00
"""Returns blog citations as html
"""
# show blog citations
2022-01-04 12:27:31 +00:00
citations_str = ''
if box_name not in ('tlblogs', 'tlfeatures'):
return citations_str
2020-12-01 10:19:53 +00:00
2021-12-25 22:09:19 +00:00
if not post_json_object['object'].get('tag'):
2022-01-04 12:27:31 +00:00
return citations_str
2020-12-01 10:19:53 +00:00
2022-01-04 12:27:31 +00:00
for tag_json in post_json_object['object']['tag']:
if not isinstance(tag_json, dict):
2020-12-01 10:19:53 +00:00
continue
2022-01-04 12:27:31 +00:00
if not tag_json.get('type'):
2020-12-01 10:19:53 +00:00
continue
2022-01-04 12:27:31 +00:00
if tag_json['type'] != 'Article':
2020-12-01 10:19:53 +00:00
continue
2022-01-04 12:27:31 +00:00
if not tag_json.get('name'):
2020-12-01 10:19:53 +00:00
continue
2022-01-04 12:27:31 +00:00
if not tag_json.get('url'):
2020-12-01 10:19:53 +00:00
continue
2022-01-04 12:27:31 +00:00
citations_str += \
2022-05-25 11:45:54 +00:00
'<li><a href="' + tag_json['url'] + '" tabindex="10">' + \
2022-01-04 12:27:31 +00:00
'<cite>' + tag_json['name'] + '</cite></a></li>\n'
2020-12-01 10:19:53 +00:00
2022-01-04 12:27:31 +00:00
if citations_str:
translated_citations_str = 'Citations'
if translate.get(translated_citations_str):
translated_citations_str = translate[translated_citations_str]
citations_str = '<p><b>' + translated_citations_str + ':</b></p>' + \
2022-06-27 16:21:48 +00:00
'<u>\n' + citations_str + '</u>\n'
2022-01-04 12:27:31 +00:00
return citations_str
2020-11-30 20:52:58 +00:00
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
"""
2022-01-04 12:27:31 +00:00
announces_str = 'announces'
if translate.get(announces_str):
announces_str = translate[announces_str]
2022-03-28 08:47:53 +00:00
return ' <img loading="lazy" decoding="async" title="' + \
2022-01-04 12:27:31 +00:00
announces_str + \
'" alt="' + announces_str + \
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: {},
nickname: str) -> 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
"""
2022-01-04 12:27:31 +00:00
announces_str = 'announces'
if translate.get(announces_str):
announces_str = translate[announces_str]
2021-12-27 11:20:57 +00:00
post_id = remove_id_ending(post_json_object['object']['id'])
post_link = '/users/' + nickname + '?convthread=' + \
post_id.replace('/', '--')
2022-03-28 08:47:53 +00:00
return ' <img loading="lazy" decoding="async" title="' + \
2022-01-04 12:27:31 +00:00
announces_str + '" alt="' + \
announces_str + '" src="/icons' + \
2020-12-01 11:12:38 +00:00
'/repeat_inactive.png" ' + \
'class="announceOrReply"/>\n' + \
' <a href="' + post_link + \
2022-05-25 11:45:54 +00:00
'" class="announceOrReply" tabindex="10">@unattributed</a>\n'
2020-12-01 11:12:38 +00:00
2021-12-29 21:55:09 +00:00
def _announce_with_display_name_html(translate: {},
post_json_object: {},
announce_display_name: str,
nickname: str) -> str:
"""Returns html for an announce having a display name
"""
2022-01-04 12:27:31 +00:00
announces_str = 'announces'
if translate.get(announces_str):
announces_str = translate[announces_str]
2021-12-27 11:20:57 +00:00
post_id = remove_id_ending(post_json_object['object']['id'])
post_link = '/users/' + nickname + '?convthread=' + \
post_id.replace('/', '--')
2022-03-28 08:47:53 +00:00
return ' <img loading="lazy" decoding="async" title="' + \
2022-01-04 12:27:31 +00:00
announces_str + '" alt="' + \
announces_str + '" src="/' + \
2020-12-09 13:08:26 +00:00
'icons/repeat_inactive.png" ' + \
'class="announceOrReply"/>\n' + \
' <a href="' + post_link + '" ' + \
2022-05-25 11:45:54 +00:00
'class="announceOrReply" tabindex="10">' + \
2022-05-02 10:21:57 +00:00
'<span itemprop="author">' + \
announce_display_name + '</span></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,
2022-01-04 12:27:31 +00:00
show_repeat_icon: bool,
is_announced: bool,
2021-12-29 21:55:09 +00:00
post_json_object: {},
2022-01-04 12:27:31 +00:00
post_actor: str,
2021-12-29 21:55:09 +00:00
translate: {},
2022-01-04 12:27:31 +00:00
enable_timing_log: bool,
post_start_time,
box_name: str,
2021-12-29 21:55:09 +00:00
person_cache: {},
2022-01-04 12:27:31 +00:00
allow_downloads: bool,
avatar_position: str,
page_number: int,
message_id_str: str,
container_class_icons: str,
container_class: str,
mitm: bool) -> (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
"""
2022-01-04 12:27:31 +00:00
title_str = ''
reply_avatar_image_in_post = ''
obj_json = post_json_object['object']
2020-12-01 11:12:38 +00:00
2021-07-21 10:48:24 +00:00
# has no attribution
2022-01-04 12:27:31 +00:00
if not obj_json.get('attributedTo'):
title_str += \
_announce_unattributed_html(translate, post_json_object, nickname)
2022-01-04 12:27:31 +00:00
return (title_str, reply_avatar_image_in_post,
container_class_icons, container_class)
2021-07-21 10:48:24 +00:00
2022-01-04 12:27:31 +00:00
attributed_to = ''
if isinstance(obj_json['attributedTo'], str):
attributed_to = obj_json['attributedTo']
2021-07-21 10:48:24 +00:00
2021-07-21 10:49:54 +00:00
# boosting your own post
2022-01-04 12:27:31 +00:00
if attributed_to.startswith(post_actor):
title_str += _boost_own_post_html(translate)
return (title_str, reply_avatar_image_in_post,
container_class_icons, container_class)
2021-07-21 10:48:24 +00:00
# boosting another person's post
2022-01-04 12:27:31 +00:00
_log_post_timing(enable_timing_log, post_start_time, '13.2')
announce_nickname = None
if attributed_to:
announce_nickname = get_nickname_from_actor(attributed_to)
if not announce_nickname:
title_str += \
_announce_unattributed_html(translate, post_json_object, nickname)
2022-01-04 12:27:31 +00:00
return (title_str, reply_avatar_image_in_post,
container_class_icons, container_class)
announce_domain, _ = get_domain_from_actor(attributed_to)
2022-06-09 16:54:44 +00:00
get_person_from_cache(base_dir, attributed_to, person_cache)
2022-01-04 12:27:31 +00:00
announce_display_name = \
get_display_name(base_dir, attributed_to, person_cache)
if announce_display_name:
if len(announce_display_name) < 2 or \
display_name_is_emoji(announce_display_name):
announce_display_name = None
2022-01-04 12:27:31 +00:00
if not announce_display_name:
announce_display_name = announce_nickname + '@' + announce_domain
_log_post_timing(enable_timing_log, post_start_time, '13.3')
2021-07-21 10:48:24 +00:00
# add any emoji to the display name
2022-01-04 12:27:31 +00:00
if ':' in announce_display_name:
announce_display_name = \
2021-12-29 21:55:09 +00:00
add_emoji_to_display_name(None, base_dir, http_prefix,
nickname, domain,
2022-07-18 16:18:04 +00:00
announce_display_name, False,
translate)
2022-01-04 12:27:31 +00:00
_log_post_timing(enable_timing_log, post_start_time, '13.3.1')
title_str += \
2021-12-29 21:55:09 +00:00
_announce_with_display_name_html(translate, post_json_object,
announce_display_name,
nickname)
if mitm:
title_str += _mitm_warning_html(translate)
2021-07-21 10:48:24 +00:00
# show avatar of person replied to
2022-01-04 12:27:31 +00:00
announce_actor = attributed_to
announce_avatar_url = \
2022-06-12 12:30:14 +00:00
get_person_avatar_url(base_dir, announce_actor, person_cache)
2021-07-21 10:48:24 +00:00
2022-01-04 12:27:31 +00:00
_log_post_timing(enable_timing_log, post_start_time, '13.4')
2021-07-21 10:48:24 +00:00
2022-01-04 12:27:31 +00:00
if not announce_avatar_url:
announce_avatar_url = ''
2021-07-21 20:38:35 +00:00
idx = 'Show options for this person'
2022-01-04 12:27:31 +00:00
if '/users/news/' not in announce_avatar_url:
show_options_for_this_person_str = idx
if translate.get(idx):
2022-01-04 12:27:31 +00:00
show_options_for_this_person_str = translate[idx]
reply_avatar_image_in_post = \
' <div class="timeline-avatar-reply">\n' \
' <a class="imageAnchor" ' + \
'href="/users/' + nickname + '?options=' + \
2022-01-04 12:27:31 +00:00
announce_actor + ';' + str(page_number) + \
2022-05-25 11:45:54 +00:00
';' + announce_avatar_url + message_id_str + \
'" tabindex="10">' \
2022-03-28 08:47:53 +00:00
'<img loading="lazy" decoding="async" src="' + \
announce_avatar_url + '" ' + \
2022-01-04 12:27:31 +00:00
'title="' + show_options_for_this_person_str + \
'" alt=" "' + avatar_position + \
2021-12-29 21:55:09 +00:00
get_broken_link_substitute() + '/></a>\n </div>\n'
2020-12-01 11:12:38 +00:00
2022-01-04 12:27:31 +00:00
return (title_str, reply_avatar_image_in_post,
container_class_icons, container_class)
2020-12-01 11:12:38 +00:00
2022-05-02 22:50:14 +00:00
def _reply_to_yourself_html(translate: {}) -> str:
"""Returns html for a title which is a reply to yourself
"""
2022-01-04 12:27:31 +00:00
replying_to_themselves_str = 'replying to themselves'
if translate.get(replying_to_themselves_str):
replying_to_themselves_str = translate[replying_to_themselves_str]
title_str = \
' <img loading="lazy" decoding="async" title="' + \
2022-01-04 12:27:31 +00:00
replying_to_themselves_str + \
'" alt="' + replying_to_themselves_str + \
2020-12-09 13:08:26 +00:00
'" src="/icons' + \
'/reply.png" class="announceOrReply"/>\n'
return title_str
2021-12-29 21:55:09 +00:00
def _reply_to_unknown_html(translate: {},
post_json_object: {},
nickname: str) -> str:
2020-12-01 13:17:51 +00:00
"""Returns the html title for a reply to an unknown handle
"""
2022-01-04 12:27:31 +00:00
replying_to_str = 'replying to'
if translate.get(replying_to_str):
replying_to_str = translate[replying_to_str]
post_id = post_json_object['object']['inReplyTo']
post_link = '/users/' + nickname + '?convthread=' + \
post_id.replace('/', '--')
2022-03-28 08:47:53 +00:00
return ' <img loading="lazy" decoding="async" title="' + \
2022-01-04 12:27:31 +00:00
replying_to_str + '" alt="' + \
replying_to_str + '" src="/icons' + \
2020-12-01 13:17:51 +00:00
'/reply.png" class="announceOrReply"/>\n' + \
' <a href="' + \
post_link + \
2022-05-25 11:45:54 +00:00
'" class="announceOrReply" tabindex="10">@unknown</a>\n'
2020-12-01 13:17:51 +00:00
def _mitm_warning_html(translate: {}) -> str:
"""Returns the html title for a reply to an unknown handle
"""
mitm_warning_str = translate['mitm']
2022-03-28 08:47:53 +00:00
return ' <img loading="lazy" decoding="async" title="' + \
mitm_warning_str + '" alt="' + \
mitm_warning_str + '" src="/icons' + \
'/mitm.png" class="announceOrReply"/>\n'
2021-12-29 21:55:09 +00:00
def _reply_with_unknown_path_html(translate: {},
post_json_object: {},
post_domain: str,
nickname: str) -> str:
"""Returns html title for a reply with an unknown path
eg. does not contain /statuses/
"""
2022-01-04 12:27:31 +00:00
replying_to_str = 'replying to'
if translate.get(replying_to_str):
replying_to_str = translate[replying_to_str]
post_id = post_json_object['object']['inReplyTo']
post_link = '/users/' + nickname + '?convthread=' + \
post_id.replace('/', '--')
2022-03-28 08:47:53 +00:00
return ' <img loading="lazy" decoding="async" title="' + \
2022-01-04 12:27:31 +00:00
replying_to_str + \
'" alt="' + replying_to_str + \
2020-12-09 13:08:26 +00:00
'" src="/icons/reply.png" ' + \
'class="announceOrReply"/>\n' + \
' <a href="' + post_link + \
2022-05-25 11:45:54 +00:00
'" class="announceOrReply" tabindex="10">' + \
2022-01-04 12:27:31 +00:00
post_domain + '</a>\n'
2021-12-29 21:55:09 +00:00
def _get_reply_html(translate: {},
in_reply_to: str, reply_display_name: str,
nickname: str) -> str:
2020-12-01 13:32:08 +00:00
"""Returns html title for a reply
"""
2022-01-04 12:27:31 +00:00
replying_to_str = 'replying to'
if translate.get(replying_to_str):
replying_to_str = translate[replying_to_str]
post_link = '/users/' + nickname + '?convthread=' + \
in_reply_to.replace('/', '--')
2020-12-01 13:32:08 +00:00
return ' ' + \
2022-03-28 08:47:53 +00:00
'<img loading="lazy" decoding="async" title="' + \
2022-01-04 12:27:31 +00:00
replying_to_str + '" alt="' + \
replying_to_str + '" 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="' + post_link + \
2022-05-25 11:45:54 +00:00
'" class="announceOrReply" tabindex="10">' + \
2022-05-02 11:00:22 +00:00
'<span itemprop="audience">' + \
2022-05-02 10:21:57 +00:00
reply_display_name + '</span></a>\n'
2020-12-01 13:32:08 +00:00
2021-12-29 21:55:09 +00:00
def _get_post_title_reply_html(base_dir: str,
http_prefix: str,
nickname: str, domain: str,
2022-01-04 12:27:31 +00:00
show_repeat_icon: bool,
is_announced: bool,
2021-12-29 21:55:09 +00:00
post_json_object: {},
2022-01-04 12:27:31 +00:00
post_actor: str,
2021-12-29 21:55:09 +00:00
translate: {},
2022-01-04 12:27:31 +00:00
enable_timing_log: bool,
post_start_time,
box_name: str,
2021-12-29 21:55:09 +00:00
person_cache: {},
2022-01-04 12:27:31 +00:00
allow_downloads: bool,
avatar_position: str,
page_number: int,
message_id_str: str,
container_class_icons: str,
container_class: str,
mitm: bool) -> (str, str, str, str):
"""Returns the reply title of a post containing names of participants
x replies to y
"""
2022-01-04 12:27:31 +00:00
title_str = ''
reply_avatar_image_in_post = ''
obj_json = post_json_object['object']
2021-07-21 10:02:42 +00:00
# not a reply
2022-01-04 12:27:31 +00:00
if not obj_json.get('inReplyTo'):
return (title_str, reply_avatar_image_in_post,
container_class_icons, container_class)
2022-01-04 12:27:31 +00:00
container_class_icons = 'containericons darker'
container_class = 'container darker'
2021-07-21 10:02:42 +00:00
# reply to self
2022-01-04 12:27:31 +00:00
if obj_json['inReplyTo'].startswith(post_actor):
2022-05-02 22:50:14 +00:00
title_str += _reply_to_yourself_html(translate)
2022-01-04 12:27:31 +00:00
return (title_str, reply_avatar_image_in_post,
container_class_icons, container_class)
2021-07-21 10:02:42 +00:00
# has a reply
2022-01-04 12:27:31 +00:00
if '/statuses/' not in obj_json['inReplyTo']:
post_domain = obj_json['inReplyTo']
2021-12-27 17:20:01 +00:00
prefixes = get_protocol_prefixes()
for prefix in prefixes:
2022-01-04 12:27:31 +00:00
post_domain = post_domain.replace(prefix, '')
if '/' in post_domain:
post_domain = post_domain.split('/', 1)[0]
if post_domain:
title_str += \
2021-12-29 21:55:09 +00:00
_reply_with_unknown_path_html(translate,
post_json_object, post_domain,
nickname)
2022-01-04 12:27:31 +00:00
return (title_str, reply_avatar_image_in_post,
container_class_icons, container_class)
in_reply_to = obj_json['inReplyTo']
reply_actor = in_reply_to.split('/statuses/')[0]
reply_nickname = get_nickname_from_actor(reply_actor)
if not reply_nickname:
title_str += \
_reply_to_unknown_html(translate, post_json_object, nickname)
2022-01-04 12:27:31 +00:00
return (title_str, reply_avatar_image_in_post,
container_class_icons, container_class)
reply_domain, _ = get_domain_from_actor(reply_actor)
if not (reply_nickname and reply_domain):
title_str += \
_reply_to_unknown_html(translate, post_json_object, nickname)
2022-01-04 12:27:31 +00:00
return (title_str, reply_avatar_image_in_post,
container_class_icons, container_class)
2022-06-09 16:54:44 +00:00
get_person_from_cache(base_dir, reply_actor, person_cache)
reply_display_name = \
get_display_name(base_dir, reply_actor, person_cache)
if reply_display_name:
if len(reply_display_name) < 2 or \
display_name_is_emoji(reply_display_name):
reply_display_name = None
2022-01-04 12:27:31 +00:00
if not reply_display_name:
reply_display_name = reply_nickname + '@' + reply_domain
2021-07-21 12:05:30 +00:00
# add emoji to the display name
2022-01-04 12:27:31 +00:00
if ':' in reply_display_name:
_log_post_timing(enable_timing_log, post_start_time, '13.5')
2021-07-21 12:05:30 +00:00
2022-01-04 12:27:31 +00:00
reply_display_name = \
2021-12-29 21:55:09 +00:00
add_emoji_to_display_name(None, base_dir, http_prefix,
nickname, domain,
2022-07-18 16:18:04 +00:00
reply_display_name, False, translate)
2022-01-04 12:27:31 +00:00
_log_post_timing(enable_timing_log, post_start_time, '13.6')
2021-07-21 12:05:30 +00:00
title_str += \
_get_reply_html(translate, in_reply_to, reply_display_name, nickname)
2021-07-21 12:05:30 +00:00
if mitm:
title_str += _mitm_warning_html(translate)
2022-01-04 12:27:31 +00:00
_log_post_timing(enable_timing_log, post_start_time, '13.7')
2021-07-21 12:05:30 +00:00
# show avatar of person replied to
2022-01-04 12:27:31 +00:00
reply_avatar_url = \
2022-06-12 12:30:14 +00:00
get_person_avatar_url(base_dir, reply_actor, person_cache)
2021-07-21 12:05:30 +00:00
2022-01-04 12:27:31 +00:00
_log_post_timing(enable_timing_log, post_start_time, '13.8')
2021-07-21 12:05:30 +00:00
2022-01-04 12:27:31 +00:00
if reply_avatar_url:
show_profile_str = 'Show profile'
if translate.get(show_profile_str):
show_profile_str = translate[show_profile_str]
reply_avatar_image_in_post = \
2021-07-21 12:05:30 +00:00
' <div class="timeline-avatar-reply">\n' + \
' <a class="imageAnchor" ' + \
2022-01-04 12:27:31 +00:00
'href="/users/' + nickname + '?options=' + reply_actor + \
';' + str(page_number) + ';' + reply_avatar_url + \
2022-05-25 11:45:54 +00:00
message_id_str + '" tabindex="10">\n' + \
2022-03-28 08:47:53 +00:00
' <img loading="lazy" decoding="async" ' + \
'src="' + reply_avatar_url + '" ' + \
2022-01-04 12:27:31 +00:00
'title="' + show_profile_str + \
'" alt=" "' + avatar_position + get_broken_link_substitute() + \
2021-07-21 12:05:30 +00:00
'/></a>\n </div>\n'
2022-01-04 12:27:31 +00:00
return (title_str, reply_avatar_image_in_post,
container_class_icons, container_class)
2021-12-29 21:55:09 +00:00
def _get_post_title_html(base_dir: str,
http_prefix: str,
nickname: str, domain: str,
2022-01-04 12:27:31 +00:00
show_repeat_icon: bool,
is_announced: bool,
2021-12-29 21:55:09 +00:00
post_json_object: {},
2022-01-04 12:27:31 +00:00
post_actor: str,
2021-12-29 21:55:09 +00:00
translate: {},
2022-01-04 12:27:31 +00:00
enable_timing_log: bool,
post_start_time,
box_name: str,
2021-12-29 21:55:09 +00:00
person_cache: {},
2022-01-04 12:27:31 +00:00
allow_downloads: bool,
avatar_position: str,
page_number: int,
message_id_str: str,
container_class_icons: str,
container_class: str,
mitm: bool) -> (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
"""
2022-01-04 12:27:31 +00:00
if not is_announced and box_name == 'search' and \
2021-12-25 22:09:19 +00:00
post_json_object.get('object'):
if post_json_object['object'].get('attributedTo'):
2022-01-04 12:27:31 +00:00
if post_json_object['object']['attributedTo'] != post_actor:
is_announced = True
2021-07-21 21:23:19 +00:00
2022-01-04 12:27:31 +00:00
if is_announced:
2021-12-29 21:55:09 +00:00
return _get_post_title_announce_html(base_dir,
http_prefix,
nickname, domain,
2022-01-04 12:27:31 +00:00
show_repeat_icon,
is_announced,
2021-12-29 21:55:09 +00:00
post_json_object,
2022-01-04 12:27:31 +00:00
post_actor,
2021-12-29 21:55:09 +00:00
translate,
2022-01-04 12:27:31 +00:00
enable_timing_log,
post_start_time,
box_name,
2021-12-29 21:55:09 +00:00
person_cache,
2022-01-04 12:27:31 +00:00
allow_downloads,
avatar_position,
page_number,
message_id_str,
container_class_icons,
container_class, mitm)
2021-12-29 21:55:09 +00:00
return _get_post_title_reply_html(base_dir,
http_prefix,
nickname, domain,
2022-01-04 12:27:31 +00:00
show_repeat_icon,
is_announced,
2021-12-29 21:55:09 +00:00
post_json_object,
2022-01-04 12:27:31 +00:00
post_actor,
2021-12-29 21:55:09 +00:00
translate,
2022-01-04 12:27:31 +00:00
enable_timing_log,
post_start_time,
box_name,
2021-12-29 21:55:09 +00:00
person_cache,
2022-01-04 12:27:31 +00:00
allow_downloads,
avatar_position,
page_number,
message_id_str,
container_class_icons,
container_class, mitm)
2022-01-04 12:27:31 +00:00
def _get_footer_with_icons(show_icons: bool,
container_class_icons: str,
reply_str: str, announce_str: str,
like_str: str, reaction_str: str,
bookmark_str: str,
delete_str: str, mute_str: str, edit_str: str,
post_json_object: {}, published_link: str,
2022-12-23 21:51:57 +00:00
time_class: str, published_str: str,
nickname: str, content_license_url: str,
translate: {}) -> str:
2020-12-01 14:08:12 +00:00
"""Returns the html for a post footer containing icons
"""
2022-01-04 12:27:31 +00:00
if not show_icons:
2020-12-01 14:08:12 +00:00
return None
2022-01-04 12:27:31 +00:00
footer_str = '\n <nav>\n'
footer_str += ' <div class="' + container_class_icons + '">\n'
footer_str += \
reply_str + announce_str + like_str + bookmark_str + reaction_str
footer_str += delete_str + mute_str + edit_str
2021-12-28 12:20:18 +00:00
if not is_news_post(post_json_object):
2022-12-27 14:05:46 +00:00
footer_str += ' '
if content_license_url:
2022-12-27 21:52:54 +00:00
footer_str += _get_copyright_footer(content_license_url,
translate)
2022-12-27 14:05:46 +00:00
# show the date
date_link = '/users/' + nickname + '?convthread=' + \
published_link.replace('/', '--')
2022-12-27 14:05:46 +00:00
footer_str += '<a href="' + date_link + '" class="' + \
2022-05-25 11:45:54 +00:00
time_class + '" tabindex="10"><span itemprop="datePublished">' + \
2022-05-02 10:21:57 +00:00
published_str + '</span></a>\n'
2020-12-01 14:08:12 +00:00
else:
2022-12-27 14:05:46 +00:00
# show the date
2022-01-04 12:27:31 +00:00
footer_str += ' <a href="' + \
published_link.replace('/news/', '/news/statuses/') + \
2022-05-25 11:45:54 +00:00
'" class="' + time_class + '" tabindex="10">' + \
'<span itemprop="datePublished">' + published_str + '</span></a>\n'
2022-01-04 12:27:31 +00:00
footer_str += ' </div>\n'
footer_str += ' </nav>\n'
return footer_str
2020-12-01 14:08:12 +00:00
def _substitute_onion_domains(base_dir: str, content: str) -> str:
"""Replace clearnet domains with onion domains
"""
# any common sites which have onion equivalents
bbc_onion = \
'bbcweb3hytmzhn5d532owbu6oqadra5z3ar726vq5kgwwn6aucdccrad.onion'
ddg_onion = \
'duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion'
guardian_onion = \
'guardian2zotagl6tmjucg3lrhxdk4dw3lhbqnkvvkywawy3oqfoprid.onion'
propublica_onion = \
'p53lf57qovyuvwsc6xnrppyply3vtqm7l6pcobkmyqsiofyeznfu5uqd.onion'
# woe betide anyone following a facebook link, but if you must
# then do it safely
facebook_onion = \
'facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd.onion'
protonmail_onion = \
'protonmailrmez3lotccipshtkleegetolb73fuirgj7r4o4vfu7ozyd.onion'
riseup_onion = \
'vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd.onion'
keybase_onion = \
'keybase5wmilwokqirssclfnsqrjdsi7jdir5wy7y7iu3tanwmtp6oid.onion'
zerobin_onion = \
'zerobinftagjpeeebbvyzjcqyjpmjvynj5qlexwyxe7l3vqejxnqv5qd.onion'
securedrop_onion = \
'sdolvtfhatvsysc6l34d65ymdwxcujausv7k5jk4cy5ttzhjoi6fzvyd.onion'
# the hell site 🔥
twitter_onion = \
'twitter3e4tixl4xyajtrzo62zg5vztmjuricljdp2c5kshju4avyoid.onion'
onion_domains = {
"bbc.com": bbc_onion,
"bbc.co.uk": bbc_onion,
"theguardian.com": guardian_onion,
"theguardian.co.uk": guardian_onion,
"duckduckgo.com": ddg_onion,
"propublica.org": propublica_onion,
"facebook.com": facebook_onion,
"protonmail.ch": protonmail_onion,
"proton.me": protonmail_onion,
"riseup.net": riseup_onion,
"keybase.io": keybase_onion,
"zerobin.net": zerobin_onion,
"securedrop.org": securedrop_onion,
"twitter.com": twitter_onion
}
onion_domains_filename = base_dir + '/accounts/onion_domains.txt'
if os.path.isfile(onion_domains_filename):
onion_domains_list = []
try:
2022-06-09 14:46:30 +00:00
with open(onion_domains_filename, 'r',
encoding='utf-8') as fp_onions:
onion_domains_list = fp_onions.readlines()
except OSError:
print('EX: unable to load onion domains file ' +
onion_domains_filename)
if onion_domains_list:
onion_domains = {}
separators = (' ', ',', '->')
for line in onion_domains_list:
line = line.strip()
if line.startswith('#'):
continue
for sep in separators:
if sep not in line:
continue
2022-06-02 10:51:55 +00:00
clearnet = line.split(sep, 1)[0].strip()
2022-06-21 11:58:50 +00:00
onion1 = line.split(sep, 1)[1].strip()
onion = remove_eol(onion1)
if clearnet and onion:
onion_domains[clearnet] = onion
break
for clearnet, onion in onion_domains.items():
if clearnet in content:
content = content.replace(clearnet, onion)
return content
2022-07-05 16:21:48 +00:00
def _add_dogwhistle_warnings(summary: str, content: str,
dogwhistles: {}, translate: {}) -> {}:
"""Adds dogwhistle warnings for the given content
"""
if not dogwhistles:
return summary
content_str = str(summary) + ' ' + content
detected = detect_dogwhistles(content_str, dogwhistles)
2022-07-05 16:21:48 +00:00
if not detected:
return summary
2022-07-05 17:52:19 +00:00
for _, item in detected.items():
2022-07-05 16:21:48 +00:00
if not item.get('category'):
continue
2022-07-05 17:52:19 +00:00
whistle_str = item['category']
if translate.get(whistle_str):
whistle_str = translate[whistle_str]
2022-07-05 16:21:48 +00:00
if summary:
2022-07-05 21:27:29 +00:00
if whistle_str not in summary:
summary += ', ' + whistle_str
2022-07-05 16:21:48 +00:00
else:
summary = whistle_str
return summary
2022-12-27 14:05:46 +00:00
def _get_content_license(post_json_object: {}) -> str:
"""Returns the content license for the given post
"""
if not post_json_object['object'].get('attachment'):
return None
for item in post_json_object['object']['attachment']:
if not item.get('name'):
continue
if not item.get('value'):
continue
if item['name'] != 'license':
continue
value = item['value']
if '://' not in value:
2022-12-27 21:30:20 +00:00
value = license_link_from_name(value)
2022-12-27 14:05:46 +00:00
return value
return None
2022-12-27 21:52:54 +00:00
def _get_copyright_footer(content_license_url: str,
translate: {}) -> str:
2022-12-27 21:52:54 +00:00
"""Returns the footer copyright link
"""
# show the CC symbol
icon_filename = 'license_cc.png'
2022-12-27 21:52:54 +00:00
if '/zero/' in content_license_url:
icon_filename = 'license_cc0.png'
2022-12-27 21:52:54 +00:00
elif 'unlicense' in content_license_url:
icon_filename = 'license_un.png'
2022-12-27 21:52:54 +00:00
elif 'wtfpl' in content_license_url:
icon_filename = 'license_wtf.png'
2022-12-27 21:52:54 +00:00
elif '/fdl' in content_license_url:
icon_filename = 'license_fdl.png'
description = translate['Content License']
copyright_str = \
' ' + \
'<a class="imageAnchor" href="' + content_license_url + \
'" title="' + description + '" tabindex="10">' + \
'<img loading="lazy" decoding="async" title="' + \
description + '" alt="' + description + \
' |" src="/icons/' + icon_filename + '"/></a>\n'
return copyright_str
2022-12-27 21:52:54 +00:00
2021-12-29 21:55:09 +00:00
def individual_post_as_html(signing_priv_key_pem: str,
2022-01-04 12:27:31 +00:00
allow_downloads: bool,
2021-12-29 21:55:09 +00:00
recent_posts_cache: {}, max_recent_posts: int,
translate: {},
2022-01-04 12:27:31 +00:00
page_number: int, base_dir: str,
2021-12-29 21:55:09 +00:00
session, cached_webfingers: {}, person_cache: {},
nickname: str, domain: str, port: int,
post_json_object: {},
2022-05-30 18:33:51 +00:00
avatar_url: str, show_avatar_options: bool,
2021-12-29 21:55:09 +00:00
allow_deletion: bool,
http_prefix: str, project_version: str,
2022-01-04 12:27:31 +00:00
box_name: str,
2021-12-29 21:55:09 +00:00
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,
2022-03-28 20:13:37 +00:00
show_repeats: bool,
2022-01-04 12:27:31 +00:00
show_icons: bool,
manually_approves_followers: bool,
show_public_only: bool,
store_to_cache: bool,
use_cache_only: bool,
2021-12-29 21:55:09 +00:00
cw_lists: {},
2022-02-25 19:12:40 +00:00
lists_enabled: str,
timezone: str,
2022-07-05 14:40:26 +00:00
mitm: bool, bold_reading: bool,
dogwhistles: {},
minimize_all_images: bool,
first_post_id: 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
2022-01-04 12:27:31 +00:00
max_reaction_types = 5
2020-11-30 23:23:21 +00:00
# benchmark
2022-01-04 12:27:31 +00:00
post_start_time = time.time()
2020-11-30 23:23:21 +00:00
2022-01-04 12:27:31 +00:00
post_actor = post_json_object['actor']
2020-11-30 23:23:21 +00:00
# ZZZzzz
2022-01-04 12:27:31 +00:00
if is_person_snoozed(base_dir, nickname, domain, post_actor):
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
2022-01-04 12:27:31 +00:00
enable_timing_log = not allow_downloads
2020-11-30 23:23:21 +00:00
2022-01-04 12:27:31 +00:00
_log_post_timing(enable_timing_log, post_start_time, '1')
2020-11-30 23:23:21 +00:00
2022-01-04 12:27:31 +00:00
avatar_position = ''
message_id = ''
2021-12-25 22:09:19 +00:00
if post_json_object.get('id'):
2022-01-04 12:27:31 +00:00
message_id = remove_hash_from_post_id(post_json_object['id'])
message_id = remove_id_ending(message_id)
2020-11-30 23:23:21 +00:00
2022-01-04 12:27:31 +00:00
_log_post_timing(enable_timing_log, post_start_time, '2')
2020-11-30 23:23:21 +00:00
2022-04-10 22:50:44 +00:00
# does this post have edits?
edits_post_url = \
remove_id_ending(message_id.strip()).replace('/', '#') + '.edits'
account_dir = acct_dir(base_dir, nickname, domain) + '/'
edits_filename = account_dir + box_name + '/' + edits_post_url
edits_str = ''
if os.path.isfile(edits_filename):
edits_json = load_json(edits_filename, 0, 1)
if edits_json:
edits_str = create_edits_html(edits_json, post_json_object,
translate, timezone, system_language)
2022-04-10 22:50:44 +00:00
2022-01-04 12:27:31 +00:00
message_id_str = ''
if message_id:
message_id_str = ';' + message_id
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
2022-01-04 12:27:31 +00:00
page_number_param = ''
if page_number:
page_number_param = '?page=' + str(page_number)
2020-11-09 19:42:09 +00:00
2020-11-30 14:45:12 +00:00
# get the html post from the recent posts cache if it exists there
2022-01-04 12:27:31 +00:00
post_html = \
2021-12-29 21:55:09 +00:00
_get_post_from_recent_cache(session, base_dir,
http_prefix, nickname, domain,
post_json_object,
2022-01-04 12:27:31 +00:00
post_actor,
2021-12-29 21:55:09 +00:00
person_cache,
2022-01-04 12:27:31 +00:00
allow_downloads,
show_public_only,
store_to_cache,
box_name,
avatar_url,
enable_timing_log,
post_start_time,
page_number,
2021-12-29 21:55:09 +00:00
recent_posts_cache,
max_recent_posts,
signing_priv_key_pem,
first_post_id)
2022-01-04 12:27:31 +00:00
if post_html:
return post_html
if use_cache_only and post_json_object['type'] != 'Announce':
return ''
2020-11-09 19:42:09 +00:00
2022-01-04 12:27:31 +00:00
_log_post_timing(enable_timing_log, post_start_time, '4')
2020-11-09 19:42:09 +00:00
2022-01-04 12:27:31 +00:00
avatar_url = \
2021-12-29 21:55:09 +00:00
get_avatar_image_url(session,
base_dir, http_prefix,
2022-01-04 12:27:31 +00:00
post_actor, person_cache,
avatar_url, allow_downloads,
2021-12-29 21:55:09 +00:00
signing_priv_key_pem)
2020-11-09 19:42:09 +00:00
2022-01-04 12:27:31 +00:00
_log_post_timing(enable_timing_log, post_start_time, '5')
2020-11-09 19:42:09 +00:00
2020-11-30 15:20:10 +00:00
# get the display name
2022-01-04 12:27:31 +00:00
if domain_full not in post_actor:
# lookup the correct webfinger for the post_actor
post_actor_nickname = get_nickname_from_actor(post_actor)
if not post_actor_nickname:
return ''
2022-01-04 12:27:31 +00:00
post_actor_domain, post_actor_port = get_domain_from_actor(post_actor)
post_actor_domain_full = \
get_full_domain(post_actor_domain, post_actor_port)
post_actor_handle = post_actor_nickname + '@' + post_actor_domain_full
post_actor_wf = \
webfinger_handle(session, post_actor_handle, http_prefix,
2021-12-29 21:55:09 +00:00
cached_webfingers,
domain, __version__, False, False,
signing_priv_key_pem)
2021-01-09 22:58:34 +00:00
2022-01-04 12:27:31 +00:00
avatar_url2 = None
display_name = None
if post_actor_wf:
origin_domain = domain
(_, _, _, _, _, avatar_url2,
display_name, _) = get_person_box(signing_priv_key_pem,
origin_domain,
base_dir, session,
post_actor_wf,
person_cache,
project_version,
http_prefix,
nickname, domain,
'outbox', 72367)
_log_post_timing(enable_timing_log, post_start_time, '6')
if avatar_url2:
avatar_url = avatar_url2
if display_name:
2020-11-30 15:20:10 +00:00
# add any emoji to the display name
2022-01-04 12:27:31 +00:00
if ':' in display_name:
display_name = \
2021-12-29 21:55:09 +00:00
add_emoji_to_display_name(session, base_dir, http_prefix,
nickname, domain,
2022-07-18 16:18:04 +00:00
display_name, False, translate)
2020-11-09 19:42:09 +00:00
2022-01-04 12:27:31 +00:00
_log_post_timing(enable_timing_log, post_start_time, '7')
2020-11-09 19:42:09 +00:00
2022-01-04 12:27:31 +00:00
avatar_link = \
2022-05-30 18:33:51 +00:00
_get_avatar_image_html(show_avatar_options,
2021-12-29 21:55:09 +00:00
nickname, domain_full,
2022-01-04 12:27:31 +00:00
avatar_url, post_actor,
translate, avatar_position,
page_number, message_id_str)
2020-11-09 19:42:09 +00:00
2022-01-04 12:27:31 +00:00
avatar_image_in_post = \
' <div class="timeline-avatar">' + avatar_link + '</div>\n'
2020-11-09 19:42:09 +00:00
2022-01-04 12:27:31 +00:00
timeline_post_bookmark = remove_id_ending(post_json_object['id'])
timeline_post_bookmark = timeline_post_bookmark.replace('://', '-')
timeline_post_bookmark = timeline_post_bookmark.replace('/', '-')
2020-11-09 19:42:09 +00:00
# If this is the inbox timeline then don't show the repeat icon on any DMs
2022-03-28 20:13:37 +00:00
show_repeat_icon = show_repeats
2022-01-04 12:27:31 +00:00
is_public_repeat = False
post_is_dm = is_dm(post_json_object)
2022-03-28 20:13:37 +00:00
if show_repeats:
2022-01-04 12:27:31 +00:00
if post_is_dm:
show_repeat_icon = False
2020-11-09 19:42:09 +00:00
else:
2021-12-28 14:41:10 +00:00
if not is_public_post(post_json_object):
2022-01-04 12:27:31 +00:00
is_public_repeat = True
2020-11-09 19:42:09 +00:00
2022-01-04 12:27:31 +00:00
title_str = ''
gallery_str = ''
is_announced = False
announce_json_object = None
2021-12-25 22:09:19 +00:00
if post_json_object['type'] == 'Announce':
2022-01-04 12:27:31 +00:00
announce_json_object = post_json_object.copy()
2021-12-31 23:07:23 +00:00
blocked_cache = {}
2022-01-04 12:27:31 +00:00
post_json_announce = \
2021-12-29 21:55:09 +00:00
download_announce(session, base_dir, http_prefix,
nickname, domain, post_json_object,
2022-05-31 13:52:46 +00:00
project_version,
2021-12-29 21:55:09 +00:00
yt_replace_domain,
twitter_replacement_domain,
allow_local_network_access,
recent_posts_cache, False,
system_language,
domain_full, person_cache,
signing_priv_key_pem,
2022-03-24 14:40:28 +00:00
blocked_cache, bold_reading)
2022-01-04 12:27:31 +00:00
if not post_json_announce:
# if the announce could not be downloaded then mark it as rejected
2022-01-04 12:27:31 +00:00
announced_post_id = remove_id_ending(post_json_object['id'])
reject_post_id(base_dir, nickname, domain, announced_post_id,
2021-12-26 20:20:36 +00:00
recent_posts_cache)
2020-11-09 19:42:09 +00:00
return ''
2022-01-04 12:27:31 +00:00
post_json_object = post_json_announce
2021-03-03 19:27:29 +00:00
2021-09-17 17:58:20 +00:00
# is the announced post in the html cache?
2022-01-04 12:27:31 +00:00
post_html = \
2021-12-29 21:55:09 +00:00
_get_post_from_recent_cache(session, base_dir,
http_prefix, nickname, domain,
post_json_object,
2022-01-04 12:27:31 +00:00
post_actor,
2021-12-29 21:55:09 +00:00
person_cache,
2022-01-04 12:27:31 +00:00
allow_downloads,
show_public_only,
store_to_cache,
box_name,
avatar_url,
enable_timing_log,
post_start_time,
page_number,
2021-12-29 21:55:09 +00:00
recent_posts_cache,
max_recent_posts,
signing_priv_key_pem,
first_post_id)
2022-01-04 12:27:31 +00:00
if post_html:
return post_html
2021-09-17 17:58:20 +00:00
2022-01-04 12:27:31 +00:00
announce_filename = \
2021-12-26 20:36:08 +00:00
locate_post(base_dir, nickname, domain, post_json_object['id'])
2022-01-04 12:27:31 +00:00
if announce_filename:
2021-12-26 23:41:34 +00:00
update_announce_collection(recent_posts_cache,
2022-01-04 12:27:31 +00:00
base_dir, announce_filename,
post_actor, nickname,
2021-12-26 23:41:34 +00:00
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'):
2022-01-04 12:27:31 +00:00
if not os.path.isfile(announce_filename + '.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'],
2022-05-12 11:56:45 +00:00
theme_name, system_language,
box_name)
2022-06-09 14:46:30 +00:00
with open(announce_filename + '.tts', 'w+',
encoding='utf-8') as ttsfile:
2022-01-04 12:27:31 +00:00
ttsfile.write('\n')
2021-03-03 19:27:29 +00:00
2022-01-04 12:27:31 +00:00
is_announced = True
2020-11-09 19:42:09 +00:00
2022-01-04 12:27:31 +00:00
_log_post_timing(enable_timing_log, post_start_time, '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
2022-01-04 12:27:31 +00:00
if show_public_only:
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 ''
2022-01-04 12:27:31 +00:00
is_moderation_post = False
2021-12-25 22:09:19 +00:00
if post_json_object['object'].get('moderationStatus'):
2022-01-04 12:27:31 +00:00
is_moderation_post = True
container_class = 'container'
container_class_icons = 'containericons'
time_class = 'time-right'
actor_nickname = get_nickname_from_actor(post_actor)
if not actor_nickname:
2020-11-09 19:42:09 +00:00
# single user instance
2022-01-04 12:27:31 +00:00
actor_nickname = 'dev'
actor_domain, _ = get_domain_from_actor(post_actor)
2020-11-09 19:42:09 +00:00
2022-01-04 12:27:31 +00:00
display_name = get_display_name(base_dir, post_actor, person_cache)
if display_name:
if len(display_name) < 2 or \
display_name_is_emoji(display_name):
display_name = None
2022-01-04 12:27:31 +00:00
if display_name:
if ':' in display_name:
display_name = \
2021-12-29 21:55:09 +00:00
add_emoji_to_display_name(session, base_dir, http_prefix,
nickname, domain,
2022-07-18 16:18:04 +00:00
display_name, False, translate)
2022-01-04 12:27:31 +00:00
title_str += \
2020-11-09 19:42:09 +00:00
' <a class="imageAnchor" href="/users/' + \
2022-01-04 12:27:31 +00:00
nickname + '?options=' + post_actor + \
';' + str(page_number) + ';' + avatar_url + message_id_str + \
2022-05-25 11:45:54 +00:00
'" tabindex="10">' + \
2022-05-02 11:00:22 +00:00
'<span itemprop="author">' + display_name + '</span>' + \
2022-05-02 10:21:57 +00:00
'</a>\n'
2020-11-09 19:42:09 +00:00
else:
2022-01-04 12:27:31 +00:00
if not message_id:
2021-12-25 22:09:19 +00:00
# pprint(post_json_object)
2022-01-04 12:27:31 +00:00
print('ERROR: no message_id')
if not actor_nickname:
2021-12-25 22:09:19 +00:00
# pprint(post_json_object)
2022-01-04 12:27:31 +00:00
print('ERROR: no actor_nickname')
if not actor_domain:
2021-12-25 22:09:19 +00:00
# pprint(post_json_object)
2022-01-04 12:27:31 +00:00
print('ERROR: no actor_domain')
2022-05-02 10:21:57 +00:00
actor_handle = actor_nickname + '@' + actor_domain
2022-01-04 12:27:31 +00:00
title_str += \
2020-11-09 19:42:09 +00:00
' <a class="imageAnchor" href="/users/' + \
2022-01-04 12:27:31 +00:00
nickname + '?options=' + post_actor + \
';' + str(page_number) + ';' + avatar_url + message_id_str + \
2022-05-25 11:45:54 +00:00
'" tabindex="10">' + \
'@<span itemprop="author">' + actor_handle + '</span></a>\n'
2020-11-09 19:42:09 +00:00
# benchmark 9
2022-01-04 12:27:31 +00:00
_log_post_timing(enable_timing_log, post_start_time, '9')
2020-11-09 19:42:09 +00:00
# Show a DM icon for DMs in the inbox timeline
2022-01-04 12:27:31 +00:00
if post_is_dm:
title_str = \
2022-03-28 08:47:53 +00:00
title_str + ' <img loading="lazy" decoding="async" 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
2022-01-04 12:27:31 +00:00
comments_enabled = 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:
2022-01-04 12:27:31 +00:00
comments_enabled = False
2021-12-25 22:09:19 +00:00
elif 'rejectReplies' in post_json_object['object']:
if post_json_object['object']['rejectReplies']:
2022-01-04 12:27:31 +00:00
comments_enabled = False
2020-11-09 19:42:09 +00:00
2022-01-04 12:27:31 +00:00
conversation_id = 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']:
2022-01-04 12:27:31 +00:00
conversation_id = post_json_object['object']['conversation']
2021-08-08 16:52:32 +00:00
2022-01-04 12:27:31 +00:00
public_reply = False
2022-03-12 14:09:36 +00:00
unlisted_reply = False
2021-12-28 14:41:10 +00:00
if is_public_post(post_json_object):
2022-01-04 12:27:31 +00:00
public_reply = True
2022-03-12 14:09:36 +00:00
if is_unlisted_post(post_json_object):
public_reply = False
unlisted_reply = True
2022-01-04 12:27:31 +00:00
reply_str = _get_reply_icon_html(base_dir, nickname, domain,
2022-03-12 14:09:36 +00:00
public_reply, unlisted_reply,
2022-01-04 12:27:31 +00:00
show_icons, comments_enabled,
post_json_object, page_number_param,
translate, system_language,
conversation_id)
2020-11-09 19:42:09 +00:00
2022-01-04 12:27:31 +00:00
_log_post_timing(enable_timing_log, post_start_time, '10')
2020-11-09 19:42:09 +00:00
2022-01-04 12:27:31 +00:00
edit_str = _get_edit_icon_html(base_dir, nickname, domain_full,
post_json_object, actor_nickname,
translate, False, first_post_id)
2022-01-04 12:27:31 +00:00
_log_post_timing(enable_timing_log, post_start_time, '11')
2020-11-30 20:52:58 +00:00
2022-01-04 12:27:31 +00:00
announce_str = \
_get_announce_icon_html(is_announced,
post_actor,
2021-12-29 21:55:09 +00:00
nickname, domain_full,
2022-01-04 12:27:31 +00:00
announce_json_object,
2021-12-29 21:55:09 +00:00
post_json_object,
2022-01-04 12:27:31 +00:00
is_public_repeat,
is_moderation_post,
show_repeat_icon,
2021-12-29 21:55:09 +00:00
translate,
2022-01-04 12:27:31 +00:00
page_number_param,
timeline_post_bookmark,
box_name, max_like_count,
first_post_id)
2021-12-29 21:55:09 +00:00
2022-01-04 12:27:31 +00:00
_log_post_timing(enable_timing_log, post_start_time, '12')
2020-11-09 19:42:09 +00:00
# whether to show a like button
2022-01-04 12:27:31 +00:00
hide_like_button_file = \
2021-12-26 12:02:29 +00:00
acct_dir(base_dir, nickname, domain) + '/.hideLikeButton'
2022-01-04 12:27:31 +00:00
show_like_button = True
if os.path.isfile(hide_like_button_file):
show_like_button = False
2020-11-09 19:42:09 +00:00
2021-11-17 14:25:24 +00:00
# whether to show a reaction button
2022-01-04 12:27:31 +00:00
hide_reaction_button_file = \
2021-12-26 12:02:29 +00:00
acct_dir(base_dir, nickname, domain) + '/.hideReactionButton'
2022-01-04 12:27:31 +00:00
show_reaction_button = True
if os.path.isfile(hide_reaction_button_file):
show_reaction_button = False
like_json_object = post_json_object
if announce_json_object:
like_json_object = announce_json_object
like_str = _get_like_icon_html(nickname, domain_full,
is_moderation_post,
show_like_button,
like_json_object,
enable_timing_log,
post_start_time,
translate, page_number_param,
timeline_post_bookmark,
box_name, max_like_count,
first_post_id)
2022-01-04 12:27:31 +00:00
_log_post_timing(enable_timing_log, post_start_time, '12.5')
bookmark_str = \
2021-12-29 21:55:09 +00:00
_get_bookmark_icon_html(nickname, domain_full,
post_json_object,
2022-01-04 12:27:31 +00:00
is_moderation_post,
2021-12-29 21:55:09 +00:00
translate,
2022-01-04 12:27:31 +00:00
enable_timing_log,
post_start_time, box_name,
page_number_param,
timeline_post_bookmark,
first_post_id)
2020-11-09 19:42:09 +00:00
2022-01-04 12:27:31 +00:00
_log_post_timing(enable_timing_log, post_start_time, '12.9')
2020-11-09 19:42:09 +00:00
2022-01-04 12:27:31 +00:00
reaction_str = \
2022-06-01 17:45:59 +00:00
_get_reaction_icon_html(nickname, post_json_object,
2022-01-04 12:27:31 +00:00
is_moderation_post,
show_reaction_button,
2021-12-29 21:55:09 +00:00
translate,
2022-01-04 12:27:31 +00:00
enable_timing_log,
post_start_time, box_name,
page_number_param,
timeline_post_bookmark,
first_post_id)
2021-11-11 15:02:03 +00:00
2022-01-04 12:27:31 +00:00
_log_post_timing(enable_timing_log, post_start_time, '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,
2022-01-04 12:27:31 +00:00
post_json_object, message_id)
2020-11-09 19:42:09 +00:00
2022-01-04 12:27:31 +00:00
_log_post_timing(enable_timing_log, post_start_time, '13')
2020-11-09 19:42:09 +00:00
2022-01-04 12:27:31 +00:00
mute_str = \
2021-12-29 21:55:09 +00:00
_get_mute_icon_html(is_muted,
2022-01-04 12:27:31 +00:00
post_actor,
message_id,
2021-12-29 21:55:09 +00:00
nickname, domain_full,
allow_deletion,
2022-01-04 12:27:31 +00:00
page_number_param,
box_name,
timeline_post_bookmark,
translate, first_post_id)
2020-11-30 17:48:35 +00:00
2022-01-04 12:27:31 +00:00
delete_str = \
2021-12-29 21:55:09 +00:00
_get_delete_icon_html(nickname, domain_full,
allow_deletion,
2022-01-04 12:27:31 +00:00
post_actor,
message_id,
2021-12-29 21:55:09 +00:00
post_json_object,
2022-01-04 12:27:31 +00:00
page_number_param,
translate, first_post_id)
2020-11-09 19:42:09 +00:00
2022-01-04 12:27:31 +00:00
_log_post_timing(enable_timing_log, post_start_time, '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
2022-01-04 12:27:31 +00:00
(title_str2,
reply_avatar_image_in_post,
container_class_icons,
container_class) = _get_post_title_html(base_dir,
http_prefix,
nickname, domain,
show_repeat_icon,
is_announced,
post_json_object,
post_actor,
translate,
enable_timing_log,
post_start_time,
box_name,
person_cache,
allow_downloads,
avatar_position,
page_number,
message_id_str,
container_class_icons,
container_class, mitm)
2022-01-04 12:27:31 +00:00
title_str += title_str2
_log_post_timing(enable_timing_log, post_start_time, '14')
person_url = local_actor_url(http_prefix, nickname, domain_full)
actor_json = \
get_person_from_cache(base_dir, person_url, person_cache)
languages_understood = []
if actor_json:
languages_understood = get_actor_languages_list(actor_json)
content_str = get_content_from_post(post_json_object, system_language,
languages_understood)
content_language = \
get_language_from_post(post_json_object, system_language,
languages_understood)
2022-11-11 11:26:17 +00:00
content_str = dont_speak_hashtags(content_str)
2022-01-04 12:27:31 +00:00
attachment_str, gallery_str = \
get_post_attachments_as_html(base_dir, nickname, domain,
domain_full,
post_json_object,
box_name, translate,
2022-01-04 12:27:31 +00:00
is_muted, avatar_link,
reply_str, announce_str, like_str,
bookmark_str, delete_str, mute_str,
content_str,
minimize_all_images)
2022-01-04 12:27:31 +00:00
published_str = \
2022-02-25 19:12:40 +00:00
_get_published_date_str(post_json_object, show_published_date_only,
timezone)
2020-11-09 19:42:09 +00:00
2022-01-04 12:27:31 +00:00
_log_post_timing(enable_timing_log, post_start_time, '15')
2020-11-09 19:42:09 +00:00
2022-01-04 12:27:31 +00:00
published_link = message_id
2020-11-09 19:42:09 +00:00
# blog posts should have no /statuses/ in their link
2022-03-24 14:22:12 +00:00
post_is_blog = False
2021-12-28 13:49:44 +00:00
if is_blog_post(post_json_object):
2022-03-24 14:22:12 +00:00
post_is_blog = True
2020-11-09 19:42:09 +00:00
# is this a post to the local domain?
2022-01-04 12:27:31 +00:00
if '://' + domain in message_id:
published_link = message_id.replace('/statuses/', '/')
2020-11-09 19:42:09 +00:00
# if this is a local link then make it relative so that it works
# on clearnet or onion address
2022-01-04 12:27:31 +00:00
if domain + '/users/' in published_link or \
domain + ':' + str(port) + '/users/' in published_link:
published_link = '/users/' + published_link.split('/users/')[1]
2020-11-09 19:42:09 +00:00
2022-12-27 21:15:42 +00:00
content_license_url = _get_content_license(post_json_object)
2021-12-28 12:20:18 +00:00
if not is_news_post(post_json_object):
if show_icons:
footer_str = ''
else:
footer_str = '<div class="' + container_class_icons + '">\n'
2022-12-27 21:15:42 +00:00
if content_license_url:
2022-12-27 21:52:54 +00:00
footer_str += _get_copyright_footer(content_license_url,
translate)
2023-01-01 14:54:12 +00:00
conv_link = '/users/' + nickname + '?convthread=' + \
published_link.replace('/', '--')
footer_str += '<a href="' + conv_link + \
2022-05-25 11:45:54 +00:00
'" class="' + time_class + '" tabindex="10">' + \
published_str + '</a>\n'
if not show_icons:
2023-01-03 17:57:52 +00:00
footer_str += '</div>\n'
2020-11-09 19:42:09 +00:00
else:
2022-01-04 12:27:31 +00:00
footer_str = '<a href="' + \
published_link.replace('/news/', '/news/statuses/') + \
2022-05-25 11:45:54 +00:00
'" class="' + time_class + '" tabindex="10">' + \
published_str + '</a>\n'
2020-11-09 19:42:09 +00:00
# change the background color for DMs in inbox timeline
2022-01-04 12:27:31 +00:00
if post_is_dm:
container_class_icons = 'containericons dm'
container_class = 'container dm'
2021-10-21 13:08:21 +00:00
# add any content warning from the cwlists directory
add_cw_from_lists(post_json_object, cw_lists, translate, lists_enabled,
system_language)
2021-10-21 13:08:21 +00:00
2022-01-04 12:27:31 +00:00
post_is_sensitive = 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'):
2022-01-04 12:27:31 +00:00
post_is_sensitive = post_json_object['object']['sensitive']
2020-11-09 19:42:09 +00:00
else:
# add a generic summary if none is provided
2022-01-04 12:27:31 +00:00
sensitive_str = 'Sensitive'
if translate.get(sensitive_str):
sensitive_str = translate[sensitive_str]
post_json_object['object']['summary'] = sensitive_str
2022-01-28 10:07:35 +00:00
post_json_object['object']['summaryMap'] = {
system_language: sensitive_str
}
2020-11-09 19:42:09 +00:00
2021-12-25 22:09:19 +00:00
if not post_json_object['object'].get('summary'):
post_json_object['object']['summary'] = ''
2022-01-28 10:07:35 +00:00
post_json_object['object']['summaryMap'] = {
system_language: ''
}
2020-11-09 19:42:09 +00:00
2022-03-24 13:14:41 +00:00
displaying_ciphertext = False
2021-12-25 22:09:19 +00:00
if post_json_object['object'].get('cipherText'):
2022-03-24 13:14:41 +00:00
displaying_ciphertext = True
2021-12-25 22:09:19 +00:00
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)
if not content_str:
content_str = get_content_from_post(post_json_object, system_language,
languages_understood)
content_language = \
get_language_from_post(post_json_object, system_language,
languages_understood)
2022-11-11 11:26:17 +00:00
content_str = dont_speak_hashtags(content_str)
2022-01-04 12:27:31 +00:00
if not content_str:
content_str = \
2021-12-29 21:55:09 +00:00
auto_translate_post(base_dir, post_json_object,
system_language, translate)
2022-01-04 12:27:31 +00:00
if not content_str:
2021-07-19 19:40:04 +00:00
return ''
2020-11-09 19:42:09 +00:00
summary_str = ''
if content_str:
2022-05-30 13:43:06 +00:00
summary_str = get_summary_from_post(post_json_object, system_language,
languages_understood)
2022-07-05 16:21:48 +00:00
# add dogwhistle warnings to summary
summary_str = _add_dogwhistle_warnings(summary_str, content_str,
dogwhistles, translate)
2022-05-30 13:43:06 +00:00
content_all_str = str(summary_str) + ' ' + content_str
# does an emoji or lack of alt text on an image indicate a
# no boost preference? if so then don't show the repeat/announce icon
attachment = []
if post_json_object['object'].get('attachment'):
attachment = post_json_object['object']['attachment']
if disallow_announce(content_all_str, attachment):
announce_str = ''
2022-03-28 21:26:39 +00:00
# does an emoji indicate a no replies preference?
# if so then don't show the reply icon
if disallow_reply(content_all_str):
reply_str = ''
new_footer_str = \
_get_footer_with_icons(show_icons,
container_class_icons,
reply_str, announce_str,
like_str, reaction_str, bookmark_str,
delete_str, mute_str, edit_str,
post_json_object, published_link,
2022-12-27 14:05:46 +00:00
time_class, published_str, nickname,
content_license_url, translate)
if new_footer_str:
footer_str = new_footer_str
# add an extra line if there is a content warning,
# for better vertical spacing on mobile
if post_is_sensitive:
footer_str = '<br>' + footer_str
if not summary_str:
summary_str = get_summary_from_post(post_json_object, system_language,
languages_understood)
2022-01-04 12:27:31 +00:00
is_patch = is_git_patch(base_dir, nickname, domain,
post_json_object['object']['type'],
2022-01-28 10:07:35 +00:00
summary_str, content_str)
2022-01-04 12:27:31 +00:00
_log_post_timing(enable_timing_log, post_start_time, '16')
if not is_pgp_encrypted(content_str):
# if we are on an onion instance then substitute any common clearnet
# domains with their onion version
if '.onion' in domain and '://' in content_str:
content_str = \
_substitute_onion_domains(base_dir, content_str)
2022-01-04 12:27:31 +00:00
if not is_patch:
# remove any tabs
content_str = \
content_str.replace('\t', '').replace('\r', '')
2022-03-24 13:35:12 +00:00
# Add bold text
2022-03-24 14:22:12 +00:00
if bold_reading and \
not displaying_ciphertext and \
not post_is_blog:
2022-03-24 13:35:12 +00:00
content_str = bold_reading_string(content_str)
2022-01-04 12:27:31 +00:00
object_content = \
remove_long_words(content_str, 40, [])
2022-03-24 14:40:28 +00:00
object_content = \
remove_text_formatting(object_content, bold_reading)
2022-01-04 12:27:31 +00:00
object_content = limit_repeated_words(object_content, 6)
object_content = \
switch_words(base_dir, nickname, domain, object_content)
object_content = html_replace_email_quote(object_content)
object_content = html_replace_quote_marks(object_content)
2022-04-13 17:31:54 +00:00
# append any edits
object_content += edits_str
else:
2022-01-04 12:27:31 +00:00
object_content = content_str
2020-11-09 19:42:09 +00:00
else:
2022-01-04 12:27:31 +00:00
encrypted_str = 'Encrypted'
if translate.get(encrypted_str):
encrypted_str = translate[encrypted_str]
object_content = '🔒 ' + encrypted_str
2022-05-02 11:16:39 +00:00
object_content = \
'<article><span itemprop="articleBody">' + \
object_content + '</span></article>'
2022-01-04 12:27:31 +00:00
if not post_is_sensitive:
content_str = object_content + attachment_str
content_str = add_embedded_elements(translate, content_str,
peertube_instances, domain)
2022-01-04 12:27:31 +00:00
content_str = insert_question(base_dir, translate,
2022-06-01 17:45:59 +00:00
nickname, domain,
2022-01-04 12:27:31 +00:00
content_str, post_json_object,
page_number)
2020-11-09 19:42:09 +00:00
else:
2022-01-04 12:27:31 +00:00
post_id = 'post' + str(create_password(8))
content_str = ''
2022-01-28 10:07:35 +00:00
if summary_str:
2022-01-04 12:27:31 +00:00
cw_str = \
2021-12-29 21:55:09 +00:00
add_emoji_to_display_name(session, base_dir, http_prefix,
nickname, domain,
2022-07-18 16:18:04 +00:00
summary_str, False, translate)
2022-01-04 12:27:31 +00:00
content_str += \
2022-05-02 11:16:39 +00:00
'<label class="cw"><span itemprop="description">' + \
cw_str + '</span></label>\n'
2022-01-04 12:27:31 +00:00
if is_moderation_post:
container_class = 'container report'
2020-11-09 19:42:09 +00:00
# get the content warning text
2022-01-04 12:27:31 +00:00
cw_content_str = object_content + attachment_str
if not is_patch:
cw_content_str = add_embedded_elements(translate, cw_content_str,
peertube_instances,
domain_full)
2022-01-04 12:27:31 +00:00
cw_content_str = \
2022-06-01 17:45:59 +00:00
insert_question(base_dir, translate, nickname, domain,
2022-01-04 12:27:31 +00:00
cw_content_str, post_json_object, page_number)
cw_content_str = \
switch_words(base_dir, nickname, domain, cw_content_str)
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
2022-01-04 12:27:31 +00:00
content_str += \
get_content_warning_button(post_id, translate, cw_content_str)
2020-11-09 19:42:09 +00:00
else:
2022-01-04 12:27:31 +00:00
content_str += cw_content_str
2020-11-09 19:42:09 +00:00
2022-01-04 12:27:31 +00:00
_log_post_timing(enable_timing_log, post_start_time, '17')
2020-11-09 19:42:09 +00:00
2022-05-21 12:05:23 +00:00
map_str = ''
if post_json_object['object'].get('tag'):
if not is_patch:
content_str = \
replace_emoji_from_tags(session, base_dir, content_str,
post_json_object['object']['tag'],
'content', False, True)
# show embedded map if the location contains a map url
location_str = \
2022-08-22 10:42:39 +00:00
get_location_from_tags(post_json_object['object']['tag'])
if location_str:
if '://' in location_str and '.' in location_str:
2022-05-21 12:28:21 +00:00
bounding_box_degrees = 0.001
2022-05-21 12:36:37 +00:00
map_str = \
html_open_street_map(location_str,
bounding_box_degrees,
translate)
2022-05-21 12:34:20 +00:00
if map_str:
map_str = '<center>\n' + map_str + '</center>\n'
2022-05-22 12:37:57 +00:00
if map_str and post_json_object['object'].get('attributedTo'):
attrib = post_json_object['object']['attributedTo']
# is this being sent by the author?
if '://' + domain_full + '/users/' + nickname in attrib:
location_domain = location_str
if '://' in location_str:
location_domain = location_str.split('://')[1]
if '/' in location_domain:
location_domain = location_domain.split('/')[0]
location_domain = \
location_str.split('://')[0] + '://' + location_domain
else:
if '/' in location_domain:
location_domain = location_domain.split('/')[0]
location_domain = 'https://' + location_domain
# remember the map site used
set_map_preferences_url(base_dir, nickname, domain,
location_domain)
# remember the coordinates
map_zoom, map_latitude, map_longitude = \
2022-05-22 14:15:04 +00:00
geocoords_from_map_link(location_str)
2022-05-22 12:37:57 +00:00
if map_zoom and map_latitude and map_longitude:
set_map_preferences_coords(base_dir, nickname, domain,
map_latitude, map_longitude,
map_zoom)
2020-11-09 19:42:09 +00:00
2021-12-29 21:55:09 +00:00
if is_muted:
2022-01-04 12:27:31 +00:00
content_str = ''
2020-11-09 19:42:09 +00:00
else:
2022-01-04 12:27:31 +00:00
if not is_patch:
message_class = 'message'
if language_right_to_left(content_language):
message_class = 'message_rtl'
content_str = ' <div class="' + message_class + '">' + \
2022-01-04 12:27:31 +00:00
content_str + \
2020-11-09 19:42:09 +00:00
' </div>\n'
else:
2022-01-04 12:27:31 +00:00
content_str = \
'<div class="gitpatch"><pre><code>' + content_str + \
2020-11-09 19:42:09 +00:00
'</code></pre></div>\n'
# show blog citations
2022-01-04 12:27:31 +00:00
citations_str = \
_get_blog_citations_html(box_name, post_json_object, translate)
post_html = ''
if box_name != 'tlmedia':
reaction_str = ''
if show_icons:
reaction_str = \
html_emoji_reactions(post_json_object, True, person_url,
max_reaction_types,
box_name, page_number)
if post_is_sensitive and reaction_str:
reaction_str = '<br>' + reaction_str
2022-05-02 10:21:57 +00:00
post_html = ' <div ' + \
'itemprop="hasPart" ' + \
2022-05-02 11:00:22 +00:00
'itemscope itemtype="http://schema.org/SocialMediaPosting" ' + \
2022-05-02 10:21:57 +00:00
'id="' + timeline_post_bookmark + \
2022-01-04 12:27:31 +00:00
'" class="' + container_class + '">\n'
post_html += avatar_image_in_post
post_html += ' <div class="post-title">\n' + \
' ' + title_str + \
reply_avatar_image_in_post + ' </div>\n'
post_html += \
2022-05-21 12:05:23 +00:00
content_str + citations_str + map_str + \
reaction_str + footer_str + '\n'
2022-01-04 12:27:31 +00:00
post_html += ' </div>\n'
2020-11-09 19:42:09 +00:00
else:
2022-01-04 12:27:31 +00:00
post_html = gallery_str
2020-11-09 19:42:09 +00:00
2022-01-04 12:27:31 +00:00
_log_post_timing(enable_timing_log, post_start_time, '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
2022-01-04 12:27:31 +00:00
if not show_public_only and store_to_cache and \
box_name != 'tlmedia' and box_name != 'tlbookmarks' and \
box_name != 'bookmarks':
2021-12-29 21:55:09 +00:00
_save_individual_post_as_html_to_cache(base_dir, nickname, domain,
2022-01-04 12:27:31 +00:00
post_json_object, post_html)
2021-12-28 14:24:14 +00:00
update_recent_posts_cache(recent_posts_cache, max_recent_posts,
2022-01-04 12:27:31 +00:00
post_json_object, post_html)
2020-11-09 19:42:09 +00:00
2022-01-04 12:27:31 +00:00
_log_post_timing(enable_timing_log, post_start_time, '19')
2020-11-09 19:42:09 +00:00
2022-01-04 12:27:31 +00:00
return post_html
2020-11-09 22:44:03 +00:00
2022-06-01 17:59:34 +00:00
def html_individual_post(recent_posts_cache: {}, max_recent_posts: int,
2021-12-29 21:55:09 +00:00
translate: {},
base_dir: str, session, cached_webfingers: {},
person_cache: {},
nickname: str, domain: str, port: int,
authorized: bool,
post_json_object: {}, http_prefix: str,
2022-06-01 17:59:34 +00:00
project_version: str, liked_by: str,
react_by: str, react_emoji: str,
2021-12-29 21:55:09 +00:00
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,
2022-02-25 19:12:40 +00:00
cw_lists: {}, lists_enabled: str,
2022-03-24 13:14:41 +00:00
timezone: str, mitm: bool,
bold_reading: bool, dogwhistles: {},
min_images_for_accounts: []) -> str:
2020-11-09 22:44:03 +00:00
"""Show an individual post as html
"""
2022-01-04 12:27:31 +00:00
original_post_json = post_json_object
post_str = ''
by_str = ''
by_text = ''
by_text_extra = ''
2022-06-01 17:59:34 +00:00
if liked_by:
by_str = liked_by
2022-01-04 12:27:31 +00:00
by_text = 'Liked by'
2022-06-01 17:59:34 +00:00
elif react_by and react_emoji:
by_str = react_by
2022-01-04 12:27:31 +00:00
by_text = 'Reaction by'
2022-06-01 17:59:34 +00:00
by_text_extra = ' ' + react_emoji
2022-01-04 12:27:31 +00:00
if by_str:
by_str_nickname = get_nickname_from_actor(by_str)
if not by_str_nickname:
return ''
2022-01-04 12:27:31 +00:00
by_str_domain, by_str_port = get_domain_from_actor(by_str)
by_str_domain = get_full_domain(by_str_domain, by_str_port)
by_str_handle = by_str_nickname + '@' + by_str_domain
if translate.get(by_text):
by_text = translate[by_text]
post_str += \
2022-05-25 11:45:54 +00:00
'<p>' + by_text + ' <a href="' + by_str + '" tabindex="10">@' + \
2022-01-04 12:27:31 +00:00
by_str_handle + '</a>' + by_text_extra + '\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
2022-01-04 12:27:31 +00:00
follow_str = ' <form method="POST" ' + \
2020-11-09 22:44:03 +00:00
'accept-charset="UTF-8" action="' + actor + '/searchhandle">\n'
2022-01-04 12:27:31 +00:00
follow_str += \
2020-11-09 22:44:03 +00:00
' <input type="hidden" name="actor" value="' + actor + '">\n'
2022-01-04 12:27:31 +00:00
follow_str += \
2020-11-09 22:44:03 +00:00
' <input type="hidden" name="searchtext" value="' + \
2022-01-04 12:27:31 +00:00
by_str_handle + '">\n'
if not is_following_actor(base_dir, nickname, domain_full, by_str):
translate_follow_str = 'Follow'
if translate.get(translate_follow_str):
translate_follow_str = translate[translate_follow_str]
follow_str += ' <button type="submit" class="button" ' + \
'name="submitSearch">' + translate_follow_str + '</button>\n'
go_back_str = 'Go Back'
if translate.get(go_back_str):
go_back_str = translate[go_back_str]
follow_str += ' <button type="submit" class="button" ' + \
'name="submitBack">' + go_back_str + '</button>\n'
follow_str += ' </form>\n'
post_str += follow_str + '</p>\n'
minimize_all_images = False
if nickname in min_images_for_accounts:
minimize_all_images = True
2022-01-04 12:27:31 +00:00
post_str += \
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,
2022-03-24 13:14:41 +00:00
cw_lists, lists_enabled, timezone, mitm,
bold_reading, dogwhistles,
minimize_all_images, None)
2022-01-04 12:27:31 +00:00
message_id = 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:
mitm = False
if os.path.isfile(post_filename.replace('.json', '') +
'.mitm'):
mitm = True
2022-01-04 12:27:31 +00:00
post_str = \
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,
2022-02-25 19:12:40 +00:00
cw_lists, lists_enabled,
2022-03-24 13:14:41 +00:00
timezone, mitm,
2022-07-05 14:40:26 +00:00
bold_reading,
dogwhistles,
minimize_all_images,
None) + post_str
2020-11-09 22:44:03 +00:00
# show the following posts
2022-01-04 12:27:31 +00:00
post_filename = locate_post(base_dir, nickname, domain, message_id)
2021-12-26 23:41:34 +00:00
if post_filename:
2020-11-09 22:44:03 +00:00
# is there a replies file for this post?
2022-01-04 12:27:31 +00:00
replies_filename = post_filename.replace('.json', '.replies')
if os.path.isfile(replies_filename):
2020-11-09 22:44:03 +00:00
# get items from the replies file
2022-01-04 12:27:31 +00:00
replies_json = {
2020-11-09 22:44:03 +00:00
'orderedItems': []
}
2021-12-28 19:33:29 +00:00
populate_replies_json(base_dir, nickname, domain,
2022-01-04 12:27:31 +00:00
replies_filename, authorized, replies_json)
2020-11-09 22:44:03 +00:00
# add items to the html output
2022-01-04 12:27:31 +00:00
for item in replies_json['orderedItems']:
post_str += \
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,
2022-02-25 19:12:40 +00:00
cw_lists, lists_enabled,
2022-03-24 13:14:41 +00:00
timezone, False,
bold_reading, dogwhistles,
minimize_all_images, None)
2021-12-31 21:18:12 +00:00
css_filename = base_dir + '/epicyon-profile.css'
2021-12-25 16:17:53 +00:00
if os.path.isfile(base_dir + '/epicyon.css'):
2021-12-31 21:18:12 +00:00
css_filename = base_dir + '/epicyon.css'
2020-11-09 22:44:03 +00:00
2022-01-04 12:27:31 +00:00
instance_title = \
2021-12-26 14:08:58 +00:00
get_config_param(base_dir, 'instanceTitle')
2022-04-13 16:32:17 +00:00
metadata_str = _html_post_metadata_open_graph(domain, original_post_json,
system_language)
2022-01-04 12:27:31 +00:00
header_str = html_header_with_external_style(css_filename,
instance_title, metadata_str)
2022-01-04 12:27:31 +00:00
return header_str + post_str + html_footer()
2021-12-29 21:55:09 +00:00
2022-06-01 17:59:34 +00:00
def html_post_replies(recent_posts_cache: {}, max_recent_posts: int,
2021-12-29 21:55:09 +00:00
translate: {}, base_dir: str,
session, cached_webfingers: {}, person_cache: {},
2022-01-04 12:27:31 +00:00
nickname: str, domain: str, port: int, replies_json: {},
2021-12-29 21:55:09 +00:00
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: {},
2022-02-25 19:12:40 +00:00
lists_enabled: str,
2022-07-05 14:40:26 +00:00
timezone: str, bold_reading: bool,
dogwhistles: {},
min_images_for_accounts: []) -> str:
2020-11-09 22:44:03 +00:00
"""Show the replies to an individual post as html
"""
2022-01-04 12:27:31 +00:00
replies_str = ''
if replies_json.get('orderedItems'):
minimize_all_images = False
if nickname in min_images_for_accounts:
minimize_all_images = True
2022-01-04 12:27:31 +00:00
for item in replies_json['orderedItems']:
replies_str += \
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,
2022-02-25 19:12:40 +00:00
cw_lists, lists_enabled,
2022-03-24 13:14:41 +00:00
timezone, False,
bold_reading, dogwhistles,
minimize_all_images, None)
2020-11-09 22:44:03 +00:00
2021-12-31 21:18:12 +00:00
css_filename = base_dir + '/epicyon-profile.css'
2021-12-25 16:17:53 +00:00
if os.path.isfile(base_dir + '/epicyon.css'):
2021-12-31 21:18:12 +00:00
css_filename = base_dir + '/epicyon.css'
2020-11-09 22:44:03 +00:00
2022-01-04 12:27:31 +00:00
instance_title = get_config_param(base_dir, 'instanceTitle')
metadata = ''
2022-01-04 12:27:31 +00:00
header_str = \
html_header_with_external_style(css_filename, instance_title, metadata)
return header_str + replies_str + html_footer()
2021-12-29 21:55:09 +00:00
2022-06-01 17:59:34 +00:00
def html_emoji_reaction_picker(recent_posts_cache: {}, max_recent_posts: int,
2021-12-29 21:55:09 +00:00
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,
2022-02-25 19:12:40 +00:00
box_name: str, page_number: int,
2022-07-05 14:40:26 +00:00
timezone: str, bold_reading: bool,
dogwhistles: {},
min_images_for_accounts: []) -> str:
2021-11-11 22:11:12 +00:00
"""Returns the emoji picker screen
"""
minimize_all_images = False
if nickname in min_images_for_accounts:
minimize_all_images = True
2022-01-04 12:27:31 +00:00
reacted_to_post_str = \
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,
2022-03-24 13:14:41 +00:00
cw_lists, lists_enabled, timezone, False,
bold_reading, dogwhistles,
minimize_all_images, None)
2021-11-11 22:11:12 +00:00
2022-01-04 12:27:31 +00:00
reactions_filename = base_dir + '/emoji/reactions.json'
if not os.path.isfile(reactions_filename):
reactions_filename = base_dir + '/emoji/default_reactions.json'
reactions_json = load_json(reactions_filename)
emoji_picks_str = ''
base_url = '/users/' + nickname
2021-12-27 11:20:57 +00:00
post_id = remove_id_ending(post_json_object['id'])
2022-01-04 12:27:31 +00:00
for _, item in reactions_json.items():
emoji_picks_str += '<div class="container">\n'
for emoji_content in item:
emoji_content_encoded = urllib.parse.quote_plus(emoji_content)
emoji_url = \
base_url + '?react=' + post_id + \
2021-12-25 22:09:19 +00:00
'?actor=' + post_json_object['actor'] + \
2022-01-04 12:27:31 +00:00
'?tl=' + box_name + \
'?page=' + str(page_number) + \
'?emojreact=' + emoji_content_encoded
emoji_label = '<label class="rlab">' + emoji_content + '</label>'
emoji_picks_str += \
2022-05-25 11:45:54 +00:00
' <a href="' + emoji_url + '" tabindex="10">' + \
emoji_label + '</a>\n'
2022-01-04 12:27:31 +00:00
emoji_picks_str += '</div>\n'
2021-11-11 22:11:12 +00:00
2021-12-31 21:18:12 +00:00
css_filename = base_dir + '/epicyon-profile.css'
2021-12-25 16:17:53 +00:00
if os.path.isfile(base_dir + '/epicyon.css'):
2021-12-31 21:18:12 +00:00
css_filename = base_dir + '/epicyon.css'
2021-11-11 22:11:12 +00:00
# filename of the banner shown at the top
2021-12-31 21:18:12 +00:00
banner_file, _ = \
2021-12-29 21:55:09 +00:00
get_banner_file(base_dir, nickname, domain, theme_name)
2022-01-04 12:27:31 +00:00
instance_title = get_config_param(base_dir, 'instanceTitle')
2021-11-11 22:11:12 +00:00
metadata = ''
2022-01-04 12:27:31 +00:00
header_str = \
html_header_with_external_style(css_filename, instance_title, metadata)
# banner
2022-01-04 12:27:31 +00:00
header_str += \
'<header>\n' + \
2022-01-04 12:27:31 +00:00
'<a href="/users/' + nickname + '/' + box_name + \
'?page=' + str(page_number) + '" title="' + \
translate['Switch to timeline view'] + '" alt="' + \
2022-05-25 11:45:54 +00:00
translate['Switch to timeline view'] + '" tabindex="10">\n'
2022-03-28 08:47:53 +00:00
header_str += '<img loading="lazy" decoding="async" ' + \
'class="timeline-banner" alt="" ' + \
2021-12-31 21:18:12 +00:00
'src="/users/' + nickname + '/' + banner_file + '" /></a>\n' + \
'</header>\n'
2022-01-04 12:27:31 +00:00
return header_str + reacted_to_post_str + emoji_picks_str + html_footer()