epicyon/webapp_post.py

2297 lines
92 KiB
Python
Raw Normal View History

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