epicyon/webapp_profile.py

2426 lines
97 KiB
Python
Raw Normal View History

2020-11-09 22:44:03 +00:00
__filename__ = "webapp_profile.py"
__author__ = "Bob Mottram"
__license__ = "AGPL3+"
2021-01-26 10:07:42 +00:00
__version__ = "1.2.0"
2020-11-09 22:44:03 +00:00
__maintainer__ = "Bob Mottram"
2021-09-10 16:14:50 +00:00
__email__ = "bob@libreserver.org"
2020-11-09 22:44:03 +00:00
__status__ = "Production"
2021-06-15 15:08:12 +00:00
__module_group__ = "Web Interface"
2020-11-09 22:44:03 +00:00
import os
from pprint import pprint
from webfinger import webfingerHandle
from utils import getDisplayName
2021-09-21 17:20:45 +00:00
from utils import isGroupAccount
from utils import hasObjectDict
2021-05-16 15:10:39 +00:00
from utils import getOccupationName
from utils import getLockedAccount
2020-12-16 11:19:16 +00:00
from utils import getFullDomain
from utils import isArtist
from utils import isDormant
2020-11-09 22:44:03 +00:00
from utils import getNicknameFromActor
from utils import getDomainFromActor
from utils import isSystemAccount
from utils import removeHtml
from utils import loadJson
from utils import getConfigParam
2020-11-21 11:21:05 +00:00
from utils import getImageFormats
2021-07-13 21:59:53 +00:00
from utils import acctDir
2021-08-08 11:16:18 +00:00
from utils import getSupportedLanguages
2021-08-14 11:13:39 +00:00
from utils import localActorUrl
from utils import getReplyIntervalHours
from languages import getActorLanguages
2020-11-09 22:44:03 +00:00
from skills import getSkills
from theme import getThemesList
from person import personBoxJson
2021-06-03 19:46:35 +00:00
from person import getActorJson
2021-06-25 14:33:16 +00:00
from person import getPersonAvatarUrl
from posts import getPersonBox
2021-10-21 19:45:39 +00:00
from posts import isModerator
2020-11-09 22:44:03 +00:00
from posts import parseUserFeed
from posts import isCreateInsideAnnounce
2020-11-09 22:44:03 +00:00
from donate import getDonationUrl
2021-08-12 20:40:23 +00:00
from donate import getWebsite
2020-11-09 22:44:03 +00:00
from xmpp import getXmppAddress
from matrix import getMatrixAddress
from ssb import getSSBAddress
from pgp import getEmailAddress
from pgp import getPGPfingerprint
from pgp import getPGPpubKey
2021-12-11 10:53:38 +00:00
from enigma import getEnigmaPubKey
2020-11-09 22:44:03 +00:00
from tox import getToxAddress
2020-12-24 16:49:57 +00:00
from briar import getBriarAddress
2020-11-29 12:50:41 +00:00
from jami import getJamiAddress
2021-06-27 11:48:03 +00:00
from cwtch import getCwtchAddress
2020-12-19 10:32:48 +00:00
from filters import isFiltered
from follow import isFollowerOfPerson
from follow import getFollowerDomains
from webapp_frontscreen import htmlFrontScreen
2021-02-05 17:05:53 +00:00
from webapp_utils import htmlKeyboardNavigation
2021-02-06 10:35:47 +00:00
from webapp_utils import htmlHideFromScreenReader
2020-11-09 22:44:03 +00:00
from webapp_utils import scheduledPostsExist
2020-11-12 17:05:38 +00:00
from webapp_utils import htmlHeaderWithExternalStyle
from webapp_utils import htmlHeaderWithPersonMarkup
2020-11-09 22:44:03 +00:00
from webapp_utils import htmlFooter
from webapp_utils import addEmojiToDisplayName
from webapp_utils import getBannerFile
from webapp_utils import htmlPostSeparator
2021-07-22 16:58:59 +00:00
from webapp_utils import editCheckBox
from webapp_utils import editTextField
from webapp_utils import editTextArea
from webapp_utils import beginEditSection
from webapp_utils import endEditSection
2021-06-26 11:16:41 +00:00
from blog import getBlogAddress
2020-11-09 22:44:03 +00:00
from webapp_post import individualPostAsHtml
from webapp_timeline import htmlIndividualShare
2021-10-21 19:19:44 +00:00
from blocking import getCWlistVariable
2020-11-09 22:44:03 +00:00
2021-12-25 22:09:19 +00:00
def _validProfilePreviewPost(post_json_object: {},
2021-11-19 13:47:02 +00:00
personUrl: str) -> (bool, {}):
"""Returns true if the given post should appear on a person/group profile
after searching for a handle
"""
isAnnouncedFeedItem = False
2021-12-25 22:09:19 +00:00
if isCreateInsideAnnounce(post_json_object):
2021-11-19 13:47:02 +00:00
isAnnouncedFeedItem = True
2021-12-25 22:09:19 +00:00
post_json_object = post_json_object['object']
if not post_json_object.get('type'):
2021-11-19 13:47:02 +00:00
return False, None
2021-12-25 22:09:19 +00:00
if post_json_object['type'] == 'Create':
if not hasObjectDict(post_json_object):
2021-11-19 13:47:02 +00:00
return False, None
2021-12-25 22:09:19 +00:00
if post_json_object['type'] != 'Create' and \
post_json_object['type'] != 'Announce':
if post_json_object['type'] != 'Note' and \
post_json_object['type'] != 'Page':
2021-11-19 13:47:02 +00:00
return False, None
2021-12-25 22:09:19 +00:00
if not post_json_object.get('to'):
2021-11-19 13:47:02 +00:00
return False, None
2021-12-25 22:09:19 +00:00
if not post_json_object.get('id'):
2021-11-19 13:47:02 +00:00
return False, None
# wrap in create
cc = []
2021-12-25 22:09:19 +00:00
if post_json_object.get('cc'):
cc = post_json_object['cc']
2021-11-19 13:47:02 +00:00
newPostJsonObject = {
2021-12-25 22:09:19 +00:00
'object': post_json_object,
'to': post_json_object['to'],
2021-11-19 13:47:02 +00:00
'cc': cc,
2021-12-25 22:09:19 +00:00
'id': post_json_object['id'],
2021-11-19 13:47:02 +00:00
'actor': personUrl,
'type': 'Create'
}
2021-12-25 22:09:19 +00:00
post_json_object = newPostJsonObject
if not post_json_object.get('actor'):
2021-11-19 13:47:02 +00:00
return False, None
if not isAnnouncedFeedItem:
2021-12-25 22:09:19 +00:00
if post_json_object['actor'] != personUrl and \
post_json_object['object']['type'] != 'Page':
2021-11-19 13:47:02 +00:00
return False, None
2021-12-25 22:09:19 +00:00
return True, post_json_object
2021-11-19 13:47:02 +00:00
2020-11-09 22:44:03 +00:00
def htmlProfileAfterSearch(cssCache: {},
2021-12-25 20:28:06 +00:00
recentPostsCache: {}, max_recent_posts: int,
2020-11-09 22:44:03 +00:00
translate: {},
2021-12-25 17:09:22 +00:00
base_dir: str, path: str, http_prefix: str,
2020-11-09 22:44:03 +00:00
nickname: str, domain: str, port: int,
profileHandle: str,
2021-12-25 22:28:18 +00:00
session, cached_webfingers: {}, person_cache: {},
2021-12-25 20:34:38 +00:00
debug: bool, 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,
2020-12-23 23:59:49 +00:00
defaultTimeline: str,
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,
accessKeys: {},
2021-12-25 23:03:28 +00:00
system_language: str,
2021-12-25 18:23:12 +00:00
max_like_count: int,
2021-12-25 23:03:28 +00:00
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 a profile page after a search for a fediverse address
"""
2021-06-03 19:46:35 +00:00
http = False
gnunet = False
2021-12-25 17:09:22 +00:00
if http_prefix == 'http':
2021-06-03 19:46:35 +00:00
http = True
2021-12-25 17:09:22 +00:00
elif http_prefix == 'gnunet':
2021-06-03 19:46:35 +00:00
gnunet = True
2021-12-26 10:08:06 +00:00
profile_json, asHeader = \
getActorJson(domain, profileHandle, http, gnunet, debug, False,
2021-12-25 23:03:28 +00:00
signing_priv_key_pem, session)
2021-12-26 10:08:06 +00:00
if not profile_json:
2020-11-09 22:44:03 +00:00
return None
2021-12-26 10:08:06 +00:00
personUrl = profile_json['id']
2021-06-03 19:46:35 +00:00
searchDomain, searchPort = getDomainFromActor(personUrl)
if not searchDomain:
return None
2021-06-03 19:46:35 +00:00
searchNickname = getNicknameFromActor(personUrl)
if not searchNickname:
return None
2020-12-16 11:19:16 +00:00
searchDomainFull = getFullDomain(searchDomain, searchPort)
2020-11-09 22:44:03 +00:00
profileStr = ''
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-09-21 18:57:12 +00:00
isGroup = False
2021-12-26 10:08:06 +00:00
if profile_json.get('type'):
if profile_json['type'] == 'Group':
2021-09-21 18:57:12 +00:00
isGroup = True
2020-11-12 17:05:38 +00:00
avatarUrl = ''
2021-12-26 10:08:06 +00:00
if profile_json.get('icon'):
if profile_json['icon'].get('url'):
avatarUrl = profile_json['icon']['url']
2020-11-12 17:05:38 +00:00
if not avatarUrl:
2021-12-25 16:17:53 +00:00
avatarUrl = getPersonAvatarUrl(base_dir, personUrl,
2021-12-25 22:17:49 +00:00
person_cache, True)
2020-11-12 17:05:38 +00:00
displayName = searchNickname
2021-12-26 10:08:06 +00:00
if profile_json.get('name'):
displayName = profile_json['name']
2021-12-26 10:08:06 +00:00
lockedAccount = getLockedAccount(profile_json)
if lockedAccount:
displayName += '🔒'
movedTo = ''
2021-12-26 10:08:06 +00:00
if profile_json.get('movedTo'):
movedTo = profile_json['movedTo']
2021-08-20 13:45:42 +00:00
if '"' in movedTo:
movedTo = movedTo.split('"')[1]
displayName += ''
followsYou = \
2021-12-25 16:17:53 +00:00
isFollowerOfPerson(base_dir,
nickname, domain,
searchNickname,
searchDomainFull)
2020-11-12 17:05:38 +00:00
profileDescription = ''
2021-12-26 10:08:06 +00:00
if profile_json.get('summary'):
profileDescription = profile_json['summary']
2020-11-12 17:05:38 +00:00
outboxUrl = None
2021-12-26 10:08:06 +00:00
if not profile_json.get('outbox'):
2020-11-12 17:05:38 +00:00
if debug:
2021-12-26 10:08:06 +00:00
pprint(profile_json)
2020-11-12 17:05:38 +00:00
print('DEBUG: No outbox found')
return None
2021-12-26 10:08:06 +00:00
outboxUrl = profile_json['outbox']
2020-11-12 17:05:38 +00:00
# profileBackgroundImage = ''
2021-12-26 10:08:06 +00:00
# if profile_json.get('image'):
# if profile_json['image'].get('url'):
# profileBackgroundImage = profile_json['image']['url']
2020-11-12 17:05:38 +00:00
# url to return to
backUrl = path
if not backUrl.endswith('/inbox'):
backUrl += '/inbox'
profileDescriptionShort = profileDescription
if '\n' in profileDescription:
if len(profileDescription.split('\n')) > 2:
2020-11-09 22:44:03 +00:00
profileDescriptionShort = ''
2020-11-12 17:05:38 +00:00
else:
if '<br>' in profileDescription:
if len(profileDescription.split('<br>')) > 2:
profileDescriptionShort = ''
# keep the profile description short
if len(profileDescriptionShort) > 256:
profileDescriptionShort = ''
# remove formatting from profile description used on title
avatarDescription = ''
2021-12-26 10:08:06 +00:00
if profile_json.get('summary'):
if isinstance(profile_json['summary'], str):
2020-11-12 17:05:38 +00:00
avatarDescription = \
2021-12-26 10:08:06 +00:00
profile_json['summary'].replace('<br>', '\n')
2020-11-12 17:05:38 +00:00
avatarDescription = avatarDescription.replace('<p>', '')
avatarDescription = avatarDescription.replace('</p>', '')
if '<' in avatarDescription:
avatarDescription = removeHtml(avatarDescription)
2020-11-12 22:41:41 +00:00
2020-11-12 23:44:16 +00:00
imageUrl = ''
2021-12-26 10:08:06 +00:00
if profile_json.get('image'):
if profile_json['image'].get('url'):
imageUrl = profile_json['image']['url']
2020-11-12 23:44:16 +00:00
2021-01-22 20:48:52 +00:00
alsoKnownAs = None
2021-12-26 10:08:06 +00:00
if profile_json.get('alsoKnownAs'):
alsoKnownAs = profile_json['alsoKnownAs']
2021-01-22 20:48:52 +00:00
joinedDate = None
2021-12-26 10:08:06 +00:00
if profile_json.get('published'):
if 'T' in profile_json['published']:
joinedDate = profile_json['published']
2020-11-12 23:38:58 +00:00
profileStr = \
2021-12-25 16:17:53 +00:00
_getProfileHeaderAfterSearch(base_dir,
nickname, defaultTimeline,
searchNickname,
searchDomainFull,
translate,
displayName, followsYou,
profileDescriptionShort,
avatarUrl, imageUrl,
2021-12-26 10:08:06 +00:00
movedTo, profile_json['id'],
alsoKnownAs, accessKeys,
joinedDate)
2020-11-12 22:41:41 +00:00
2021-12-26 10:00:46 +00:00
domain_full = getFullDomain(domain, port)
2020-11-20 12:14:22 +00:00
2020-11-20 12:20:34 +00:00
followIsPermitted = True
2021-12-26 10:08:06 +00:00
if not profile_json.get('followers'):
# no followers collection specified within actor
followIsPermitted = False
2021-12-26 10:00:46 +00:00
elif searchNickname == 'news' and searchDomainFull == domain_full:
2020-11-20 12:20:34 +00:00
# currently the news actor is not something you can follow
followIsPermitted = False
2021-12-26 10:00:46 +00:00
elif searchNickname == nickname and searchDomainFull == domain_full:
2020-11-20 12:20:34 +00:00
# don't follow yourself!
followIsPermitted = False
if followIsPermitted:
2021-09-21 18:57:12 +00:00
followStr = 'Follow'
if isGroup:
followStr = 'Join'
2020-11-20 12:14:22 +00:00
profileStr += \
2021-07-22 10:22:01 +00:00
'<div class="container">\n' + \
' <form method="POST" action="' + \
backUrl + '/followconfirm">\n' + \
' <center>\n' + \
2020-11-20 12:14:22 +00:00
' <input type="hidden" name="actor" value="' + \
2021-07-22 10:22:01 +00:00
personUrl + '">\n' + \
2021-04-23 12:04:42 +00:00
' <button type="submit" class="button" name="submitYes" ' + \
'accesskey="' + accessKeys['followButton'] + '">' + \
2021-09-21 18:57:12 +00:00
translate[followStr] + '</button>\n' + \
2021-07-28 14:02:07 +00:00
' <button type="submit" class="button" name="submitView" ' + \
'accesskey="' + accessKeys['viewButton'] + '">' + \
translate['View'] + '</button>\n' + \
' </center>\n' + \
' </form>\n' + \
'</div>\n'
else:
profileStr += \
'<div class="container">\n' + \
' <form method="POST" action="' + \
backUrl + '/followconfirm">\n' + \
' <center>\n' + \
' <input type="hidden" name="actor" value="' + \
personUrl + '">\n' + \
' <button type="submit" class="button" name="submitView" ' + \
'accesskey="' + accessKeys['viewButton'] + '">' + \
translate['View'] + '</button>\n' + \
' </center>\n' + \
' </form>\n' + \
'</div>\n'
2020-11-12 17:05:38 +00:00
2021-08-02 20:17:55 +00:00
userFeed = \
2021-12-25 23:03:28 +00:00
parseUserFeed(signing_priv_key_pem,
2021-12-25 20:34:38 +00:00
session, outboxUrl, asHeader, project_version,
2021-12-25 17:09:22 +00:00
http_prefix, domain, debug)
2021-08-02 20:17:55 +00:00
if userFeed:
i = 0
for item in userFeed:
2021-12-25 22:09:19 +00:00
showItem, post_json_object = \
2021-11-19 13:47:02 +00:00
_validProfilePreviewPost(item, personUrl)
if not showItem:
continue
2020-12-18 18:12:33 +00:00
2021-08-02 20:17:55 +00:00
profileStr += \
2021-12-25 23:03:28 +00:00
individualPostAsHtml(signing_priv_key_pem,
True, recentPostsCache,
max_recent_posts,
2021-12-25 16:17:53 +00:00
translate, None, base_dir,
2021-12-25 22:28:18 +00:00
session, cached_webfingers, person_cache,
2021-08-02 20:17:55 +00:00
nickname, domain, port,
2021-12-25 23:03:28 +00:00
post_json_object, avatarUrl,
False, 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 23:03:28 +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)
2021-08-02 20:17:55 +00:00
i += 1
if i >= 8:
2021-08-02 20:17:55 +00:00
break
2020-11-09 22:44:03 +00:00
2021-01-11 19:46:21 +00:00
instanceTitle = \
2021-12-25 16:17:53 +00:00
getConfigParam(base_dir, 'instanceTitle')
return htmlHeaderWithExternalStyle(cssFilename, instanceTitle, None) + \
2021-01-11 19:46:21 +00:00
profileStr + htmlFooter()
2020-11-09 22:44:03 +00:00
2021-12-25 17:09:22 +00:00
def _getProfileHeader(base_dir: str, http_prefix: str,
nickname: str, domain: str,
2021-12-26 10:00:46 +00:00
domain_full: str, translate: {},
defaultTimeline: str,
displayName: str,
avatarDescription: str,
profileDescriptionShort: str,
loginButton: str, avatarUrl: str,
2021-01-22 20:59:43 +00:00
theme: str, movedTo: str,
2021-01-24 18:09:21 +00:00
alsoKnownAs: [],
2021-04-23 12:04:42 +00:00
pinnedContent: str,
accessKeys: {},
2021-05-12 18:31:41 +00:00
joinedDate: str,
occupationName: str) -> str:
2020-11-12 22:33:00 +00:00
"""The header of the profile screen, containing background
image and avatar
"""
2021-07-22 10:22:01 +00:00
htmlStr = \
'\n\n <figure class="profileHeader">\n' + \
' <a href="/users/' + \
2020-11-12 22:33:00 +00:00
nickname + '/' + defaultTimeline + '" title="' + \
2021-07-22 10:22:01 +00:00
translate['Switch to timeline view'] + '">\n' + \
' <img class="profileBackground" ' + \
'alt="" ' + \
2021-07-22 10:22:01 +00:00
'src="/users/' + nickname + '/image_' + theme + '.png" /></a>\n' + \
' <figcaption>\n' + \
2020-11-12 23:17:37 +00:00
' <a href="/users/' + \
nickname + '/' + defaultTimeline + '" title="' + \
translate['Switch to timeline view'] + '">\n' + \
' <img loading="lazy" src="' + avatarUrl + '" ' + \
'alt="" class="title"></a>\n'
2021-05-12 18:31:41 +00:00
occupationStr = ''
if occupationName:
occupationStr += \
' <b>' + occupationName + '</b><br>\n'
htmlStr += ' <h1>' + displayName + '</h1>\n' + occupationStr
2020-11-12 22:33:00 +00:00
htmlStr += \
2021-12-26 10:00:46 +00:00
' <p><b>@' + nickname + '@' + domain_full + '</b><br>\n'
if joinedDate:
htmlStr += \
2021-05-08 18:00:59 +00:00
' <p>' + translate['Joined'] + ' ' + \
joinedDate.split('T')[0] + '<br>\n'
if movedTo:
newNickname = getNicknameFromActor(movedTo)
newDomain, newPort = getDomainFromActor(movedTo)
newDomainFull = getFullDomain(newDomain, newPort)
if newNickname and newDomain:
htmlStr += \
' <p>' + translate['New account'] + ': ' + \
'<a href="' + movedTo + '">@' + \
newNickname + '@' + newDomainFull + '</a><br>\n'
2021-01-22 20:59:43 +00:00
elif alsoKnownAs:
otherAccountsHtml = \
2021-01-22 20:59:43 +00:00
' <p>' + translate['Other accounts'] + ': '
2021-12-26 10:00:46 +00:00
actor = localActorUrl(http_prefix, nickname, domain_full)
ctr = 0
2021-01-22 20:59:43 +00:00
if isinstance(alsoKnownAs, list):
for altActor in alsoKnownAs:
if altActor == actor:
continue
2021-01-22 20:59:43 +00:00
if ctr > 0:
otherAccountsHtml += ' '
2021-01-22 20:59:43 +00:00
ctr += 1
altDomain, altPort = getDomainFromActor(altActor)
otherAccountsHtml += \
2021-01-22 20:59:43 +00:00
'<a href="' + altActor + '">' + altDomain + '</a>'
elif isinstance(alsoKnownAs, str):
if alsoKnownAs != actor:
ctr += 1
altDomain, altPort = getDomainFromActor(alsoKnownAs)
otherAccountsHtml += \
'<a href="' + alsoKnownAs + '">' + altDomain + '</a>'
otherAccountsHtml += '</p>\n'
if ctr > 0:
htmlStr += otherAccountsHtml
2020-11-12 22:33:00 +00:00
htmlStr += \
2020-11-12 23:00:36 +00:00
' <a href="/users/' + nickname + \
2020-11-12 22:33:00 +00:00
'/qrcode.png" alt="' + translate['QR Code'] + '" title="' + \
translate['QR Code'] + '">' + \
2021-02-01 18:42:39 +00:00
'<img class="qrcode" alt="' + translate['QR Code'] + \
2021-07-22 10:22:01 +00:00
'" src="/icons/qrcode.png" /></a></p>\n' + \
' <p>' + profileDescriptionShort + '</p>\n' + loginButton
2021-01-24 18:09:21 +00:00
if pinnedContent:
2021-01-28 14:21:51 +00:00
htmlStr += pinnedContent.replace('<p>', '<p>📎', 1)
2021-07-22 10:22:01 +00:00
htmlStr += \
' </figcaption>\n' + \
' </figure>\n\n'
2020-11-12 22:33:00 +00:00
return htmlStr
2021-12-25 16:17:53 +00:00
def _getProfileHeaderAfterSearch(base_dir: str,
nickname: str, defaultTimeline: str,
searchNickname: str,
searchDomainFull: str,
translate: {},
displayName: str,
followsYou: bool,
profileDescriptionShort: str,
avatarUrl: str, imageUrl: str,
movedTo: str, actor: str,
2021-04-23 12:04:42 +00:00
alsoKnownAs: [],
accessKeys: {},
joinedDate: str) -> str:
2020-11-12 23:38:58 +00:00
"""The header of a searched for handle, containing background
image and avatar
"""
if not imageUrl:
imageUrl = '/defaultprofilebackground'
2021-07-22 10:22:01 +00:00
htmlStr = \
'\n\n <figure class="profileHeader">\n' + \
' <a href="/users/' + \
nickname + '/' + defaultTimeline + '" title="' + \
2021-04-23 12:04:42 +00:00
translate['Switch to timeline view'] + '" ' + \
2021-07-22 10:22:01 +00:00
'accesskey="' + accessKeys['menuTimeline'] + '">\n' + \
' <img class="profileBackground" ' + \
'alt="" ' + \
2021-07-22 10:22:01 +00:00
'src="' + imageUrl + '" /></a>\n' + \
' <figcaption>\n'
2020-11-12 23:38:58 +00:00
if avatarUrl:
htmlStr += \
2021-07-22 10:22:01 +00:00
' <a href="/users/' + \
nickname + '/' + defaultTimeline + '" title="' + \
translate['Switch to timeline view'] + '">\n' + \
2020-11-12 23:38:58 +00:00
' <img loading="lazy" src="' + avatarUrl + '" ' + \
'alt="" class="title"></a>\n'
2021-09-13 13:47:00 +00:00
if not displayName:
displayName = searchNickname
2020-11-12 23:38:58 +00:00
htmlStr += \
2021-07-22 10:22:01 +00:00
' <h1>' + displayName + '</h1>\n' + \
2020-11-12 23:38:58 +00:00
' <p><b>@' + searchNickname + '@' + searchDomainFull + '</b><br>\n'
if joinedDate:
2021-05-08 18:00:59 +00:00
htmlStr += ' <p>' + translate['Joined'] + ' ' + \
joinedDate.split('T')[0] + '</p>\n'
if followsYou:
htmlStr += ' <p><b>' + translate['Follows you'] + '</b></p>\n'
if movedTo:
newNickname = getNicknameFromActor(movedTo)
newDomain, newPort = getDomainFromActor(movedTo)
newDomainFull = getFullDomain(newDomain, newPort)
if newNickname and newDomain:
newHandle = newNickname + '@' + newDomainFull
htmlStr += ' <p>' + translate['New account'] + \
2021-08-20 13:51:33 +00:00
': <a href="' + movedTo + '">@' + newHandle + '</a></p>\n'
2021-01-22 20:48:52 +00:00
elif alsoKnownAs:
otherAccountshtml = \
2021-01-22 20:48:52 +00:00
' <p>' + translate['Other accounts'] + ': '
ctr = 0
2021-01-22 20:48:52 +00:00
if isinstance(alsoKnownAs, list):
for altActor in alsoKnownAs:
if altActor == actor:
continue
2021-01-22 20:48:52 +00:00
if ctr > 0:
otherAccountshtml += ' '
2021-01-22 20:48:52 +00:00
ctr += 1
altDomain, altPort = getDomainFromActor(altActor)
otherAccountshtml += \
2021-01-22 20:48:52 +00:00
'<a href="' + altActor + '">' + altDomain + '</a>'
elif isinstance(alsoKnownAs, str):
if alsoKnownAs != actor:
ctr += 1
altDomain, altPort = getDomainFromActor(alsoKnownAs)
otherAccountshtml += \
'<a href="' + alsoKnownAs + '">' + altDomain + '</a>'
otherAccountshtml += '</p>\n'
if ctr > 0:
htmlStr += otherAccountshtml
2021-07-22 10:22:01 +00:00
htmlStr += \
' <p>' + profileDescriptionShort + '</p>\n' + \
' </figcaption>\n' + \
' </figure>\n\n'
2020-11-12 23:38:58 +00:00
return htmlStr
2021-12-25 23:03:28 +00:00
def htmlProfile(signing_priv_key_pem: str,
2021-12-25 19:09:03 +00:00
rss_icon_at_top: bool,
2021-12-25 19:19:14 +00:00
cssCache: {}, icons_as_buttons: bool,
2020-11-09 22:44:03 +00:00
defaultTimeline: str,
2021-12-25 20:28:06 +00:00
recentPostsCache: {}, max_recent_posts: int,
2021-12-25 20:34:38 +00:00
translate: {}, project_version: str,
2021-12-25 17:09:22 +00:00
base_dir: str, http_prefix: str, authorized: bool,
2021-12-26 10:08:06 +00:00
profile_json: {}, selected: str,
2021-12-25 22:28:18 +00:00
session, cached_webfingers: {}, person_cache: {},
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 18:47:04 +00:00
newswire: {}, theme: str, dormant_months: int,
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:09:49 +00:00
text_mode_banner: str,
debug: bool, accessKeys: {}, city: str,
2021-12-25 23:03:28 +00:00
system_language: str, max_like_count: int,
2021-12-25 18:05:01 +00:00
shared_items_federated_domains: [],
2021-10-21 13:08:21 +00:00
extraJson: {}, pageNumber: int,
maxItemsPerPage: int,
2021-12-25 23:26:38 +00:00
cw_lists: {}, lists_enabled: str,
2021-12-25 17:13:38 +00:00
content_license_url: str) -> str:
2020-11-09 22:44:03 +00:00
"""Show the profile page as html
"""
2021-12-26 10:08:06 +00:00
nickname = profile_json['preferredUsername']
2020-11-09 22:44:03 +00:00
if not nickname:
return ""
if isSystemAccount(nickname):
2021-12-25 23:03:28 +00:00
return htmlFrontScreen(signing_priv_key_pem,
2021-12-25 19:09:03 +00:00
rss_icon_at_top,
2021-12-25 19:19:14 +00:00
cssCache, icons_as_buttons,
defaultTimeline,
2021-12-25 20:28:06 +00:00
recentPostsCache, max_recent_posts,
2021-12-25 20:34:38 +00:00
translate, project_version,
2021-12-25 17:09:22 +00:00
base_dir, http_prefix, authorized,
2021-12-26 10:08:06 +00:00
profile_json, selected,
2021-12-25 22:28:18 +00:00
session, cached_webfingers, person_cache,
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,
2020-12-20 17:26:38 +00:00
newswire, theme, extraJson,
2021-12-25 18:54:50 +00:00
allow_local_network_access, accessKeys,
2021-12-25 23:03:28 +00:00
system_language, max_like_count,
2021-12-25 18:05:01 +00:00
shared_items_federated_domains, None,
2021-12-25 23:26:38 +00:00
pageNumber, maxItemsPerPage, cw_lists,
2021-12-25 18:12:13 +00:00
lists_enabled)
2021-12-26 10:08:06 +00:00
domain, port = getDomainFromActor(profile_json['id'])
2020-11-09 22:44:03 +00:00
if not domain:
return ""
displayName = \
2021-12-25 17:09:22 +00:00
addEmojiToDisplayName(session, base_dir, http_prefix,
2020-11-09 22:44:03 +00:00
nickname, domain,
2021-12-26 10:08:06 +00:00
profile_json['name'], True)
2021-12-26 10:00:46 +00:00
domain_full = getFullDomain(domain, port)
2020-11-09 22:44:03 +00:00
profileDescription = \
2021-12-25 17:09:22 +00:00
addEmojiToDisplayName(session, base_dir, http_prefix,
2020-11-09 22:44:03 +00:00
nickname, domain,
2021-12-26 10:08:06 +00:00
profile_json['summary'], False)
2020-11-09 22:44:03 +00:00
postsButton = 'button'
followingButton = 'button'
followersButton = 'button'
rolesButton = 'button'
skillsButton = 'button'
sharesButton = 'button'
2021-08-09 19:37:18 +00:00
wantedButton = 'button'
2020-11-09 22:44:03 +00:00
if selected == 'posts':
postsButton = 'buttonselected'
elif selected == 'following':
followingButton = 'buttonselected'
elif selected == 'followers':
followersButton = 'buttonselected'
elif selected == 'roles':
rolesButton = 'buttonselected'
elif selected == 'skills':
skillsButton = 'buttonselected'
elif selected == 'shares':
sharesButton = 'buttonselected'
2021-08-09 19:37:18 +00:00
elif selected == 'wanted':
wantedButton = 'buttonselected'
2020-11-09 22:44:03 +00:00
loginButton = ''
followApprovalsSection = ''
followApprovals = False
editProfileStr = ''
logoutStr = ''
2021-12-26 10:08:06 +00:00
actor = profile_json['id']
2020-11-09 22:44:03 +00:00
usersPath = '/users/' + actor.split('/users/')[1]
donateSection = ''
2021-12-26 10:08:06 +00:00
donateUrl = getDonationUrl(profile_json)
websiteUrl = getWebsite(profile_json, translate)
blogAddress = getBlogAddress(profile_json)
EnigmaPubKey = getEnigmaPubKey(profile_json)
PGPpubKey = getPGPpubKey(profile_json)
PGPfingerprint = getPGPfingerprint(profile_json)
emailAddress = getEmailAddress(profile_json)
xmppAddress = getXmppAddress(profile_json)
matrixAddress = getMatrixAddress(profile_json)
ssbAddress = getSSBAddress(profile_json)
toxAddress = getToxAddress(profile_json)
briarAddress = getBriarAddress(profile_json)
jamiAddress = getJamiAddress(profile_json)
cwtchAddress = getCwtchAddress(profile_json)
2021-08-12 21:08:55 +00:00
if donateUrl or websiteUrl or xmppAddress or matrixAddress or \
2020-12-24 16:48:03 +00:00
ssbAddress or toxAddress or briarAddress or \
2021-12-11 10:53:38 +00:00
jamiAddress or cwtchAddress or PGPpubKey or EnigmaPubKey or \
2020-11-09 22:44:03 +00:00
PGPfingerprint or emailAddress:
donateSection = '<div class="container">\n'
donateSection += ' <center>\n'
if donateUrl and not isSystemAccount(nickname):
donateSection += \
' <p><a href="' + donateUrl + \
'"><button class="donateButton">' + translate['Donate'] + \
'</button></a></p>\n'
2021-08-12 21:08:55 +00:00
if websiteUrl:
donateSection += \
'<p>' + translate['Website'] + ': <a href="' + \
websiteUrl + '">' + websiteUrl + '</a></p>\n'
2020-11-09 22:44:03 +00:00
if emailAddress:
donateSection += \
'<p>' + translate['Email'] + ': <a href="mailto:' + \
emailAddress + '">' + emailAddress + '</a></p>\n'
2021-08-20 13:56:52 +00:00
if blogAddress:
donateSection += \
'<p>Blog: <a href="' + \
blogAddress + '">' + blogAddress + '</a></p>\n'
2020-11-09 22:44:03 +00:00
if xmppAddress:
donateSection += \
'<p>' + translate['XMPP'] + ': <a href="xmpp:' + \
2021-06-22 12:42:52 +00:00
xmppAddress + '">' + xmppAddress + '</a></p>\n'
2020-11-09 22:44:03 +00:00
if matrixAddress:
donateSection += \
'<p>' + translate['Matrix'] + ': ' + matrixAddress + '</p>\n'
if ssbAddress:
donateSection += \
'<p>SSB: <label class="ssbaddr">' + \
ssbAddress + '</label></p>\n'
if toxAddress:
donateSection += \
'<p>Tox: <label class="toxaddr">' + \
toxAddress + '</label></p>\n'
2020-12-24 16:48:03 +00:00
if briarAddress:
2020-12-24 17:11:18 +00:00
if briarAddress.startswith('briar://'):
donateSection += \
'<p><label class="toxaddr">' + \
briarAddress + '</label></p>\n'
else:
donateSection += \
'<p>briar://<label class="toxaddr">' + \
briarAddress + '</label></p>\n'
2020-11-29 12:50:41 +00:00
if jamiAddress:
donateSection += \
'<p>Jami: <label class="toxaddr">' + \
jamiAddress + '</label></p>\n'
2021-06-27 11:48:03 +00:00
if cwtchAddress:
donateSection += \
'<p>Cwtch: <label class="toxaddr">' + \
cwtchAddress + '</label></p>\n'
2021-12-11 10:53:38 +00:00
if EnigmaPubKey:
donateSection += \
'<p>Enigma: <label class="toxaddr">' + \
EnigmaPubKey + '</label></p>\n'
2020-11-09 22:44:03 +00:00
if PGPfingerprint:
donateSection += \
'<p class="pgp">PGP: ' + \
PGPfingerprint.replace('\n', '<br>') + '</p>\n'
if PGPpubKey:
donateSection += \
'<p class="pgp">' + PGPpubKey.replace('\n', '<br>') + '</p>\n'
donateSection += ' </center>\n'
donateSection += '</div>\n'
if authorized:
2020-11-09 22:44:03 +00:00
editProfileStr = \
'<a class="imageAnchor" href="' + usersPath + '/editprofile">' + \
2020-12-09 13:08:26 +00:00
'<img loading="lazy" src="/icons' + \
2020-11-09 22:44:03 +00:00
'/edit.png" title="' + translate['Edit'] + \
'" alt="| ' + translate['Edit'] + '" class="timelineicon"/></a>\n'
logoutStr = \
'<a class="imageAnchor" href="/logout">' + \
2020-12-09 13:08:26 +00:00
'<img loading="lazy" src="/icons' + \
2020-11-09 22:44:03 +00:00
'/logout.png" title="' + translate['Logout'] + \
'" alt="| ' + translate['Logout'] + \
'" class="timelineicon"/></a>\n'
# are there any follow requests?
followRequestsFilename = \
2021-12-25 16:17:53 +00:00
acctDir(base_dir, nickname, domain) + '/followrequests.txt'
2020-11-09 22:44:03 +00:00
if os.path.isfile(followRequestsFilename):
with open(followRequestsFilename, 'r') as f:
for line in f:
if len(line) > 0:
followApprovals = True
followersButton = 'buttonhighlighted'
if selected == 'followers':
followersButton = 'buttonselectedhighlighted'
break
if selected == 'followers':
if followApprovals:
currFollowerDomains = \
2021-12-25 16:17:53 +00:00
getFollowerDomains(base_dir, nickname, domain)
2020-11-09 22:44:03 +00:00
with open(followRequestsFilename, 'r') as f:
for followerHandle in f:
if len(line) > 0:
2021-11-08 18:39:17 +00:00
followerHandle = followerHandle.replace('\n', '')
2020-11-09 22:44:03 +00:00
if '://' in followerHandle:
followerActor = followerHandle
else:
2021-08-14 11:13:39 +00:00
nick = followerHandle.split('@')[0]
dom = followerHandle.split('@')[1]
2020-11-09 22:44:03 +00:00
followerActor = \
2021-12-25 17:09:22 +00:00
localActorUrl(http_prefix, nick, dom)
# is this a new domain?
# if so then append a new instance indicator
followerDomain, _ = \
getDomainFromActor(followerActor)
newFollowerDomain = ''
if followerDomain not in currFollowerDomains:
newFollowerDomain = ''
2020-11-09 22:44:03 +00:00
basePath = '/users/' + nickname
followApprovalsSection += '<div class="container">'
followApprovalsSection += \
'<a href="' + followerActor + '">'
followApprovalsSection += \
'<span class="followRequestHandle">' + \
followerHandle + \
newFollowerDomain + '</span></a>'
# show Approve and Deny buttons
2020-11-09 22:44:03 +00:00
followApprovalsSection += \
'<a href="' + basePath + \
'/followapprove=' + followerHandle + '">'
followApprovalsSection += \
'<button class="followApprove">' + \
translate['Approve'] + '</button></a><br><br>'
followApprovalsSection += \
'<a href="' + basePath + \
'/followdeny=' + followerHandle + '">'
followApprovalsSection += \
'<button class="followDeny">' + \
translate['Deny'] + '</button></a>'
followApprovalsSection += '</div>'
profileDescriptionShort = profileDescription
if '\n' in profileDescription:
if len(profileDescription.split('\n')) > 2:
profileDescriptionShort = ''
else:
if '<br>' in profileDescription:
if len(profileDescription.split('<br>')) > 2:
profileDescriptionShort = ''
profileDescription = profileDescription.replace('<br>', '\n')
# keep the profile description short
if len(profileDescriptionShort) > 256:
profileDescriptionShort = ''
# remove formatting from profile description used on title
avatarDescription = ''
2021-12-26 10:08:06 +00:00
if profile_json.get('summary'):
avatarDescription = profile_json['summary'].replace('<br>', '\n')
2020-11-09 22:44:03 +00:00
avatarDescription = avatarDescription.replace('<p>', '')
avatarDescription = avatarDescription.replace('</p>', '')
movedTo = ''
2021-12-26 10:08:06 +00:00
if profile_json.get('movedTo'):
movedTo = profile_json['movedTo']
2021-08-20 13:45:42 +00:00
if '"' in movedTo:
movedTo = movedTo.split('"')[1]
2021-01-22 20:59:43 +00:00
alsoKnownAs = None
2021-12-26 10:08:06 +00:00
if profile_json.get('alsoKnownAs'):
alsoKnownAs = profile_json['alsoKnownAs']
2021-01-22 20:59:43 +00:00
joinedDate = None
2021-12-26 10:08:06 +00:00
if profile_json.get('published'):
if 'T' in profile_json['published']:
joinedDate = profile_json['published']
2021-05-12 18:31:41 +00:00
occupationName = None
2021-12-26 10:08:06 +00:00
if profile_json.get('hasOccupation'):
occupationName = getOccupationName(profile_json)
2021-12-26 10:08:06 +00:00
avatarUrl = profile_json['icon']['url']
2021-06-13 08:48:59 +00:00
# use alternate path for local avatars to avoid any caching issues
2021-12-26 10:00:46 +00:00
if '://' + domain_full + '/system/accounts/avatars/' in avatarUrl:
2021-06-13 08:48:59 +00:00
avatarUrl = \
2021-12-26 10:00:46 +00:00
avatarUrl.replace('://' + domain_full +
'/system/accounts/avatars/',
'://' + domain_full + '/users/')
2021-01-24 18:09:21 +00:00
# get pinned post content
2021-12-25 16:17:53 +00:00
accountDir = acctDir(base_dir, nickname, domain)
2021-01-24 18:09:21 +00:00
pinnedFilename = accountDir + '/pinToProfile.txt'
pinnedContent = None
if os.path.isfile(pinnedFilename):
with open(pinnedFilename, 'r') as pinFile:
pinnedContent = pinFile.read()
2021-01-24 18:09:21 +00:00
profileHeaderStr = \
2021-12-25 17:09:22 +00:00
_getProfileHeader(base_dir, http_prefix,
nickname, domain,
2021-12-26 10:00:46 +00:00
domain_full, translate,
defaultTimeline, displayName,
avatarDescription,
profileDescriptionShort,
loginButton, avatarUrl, theme,
2021-01-24 18:09:21 +00:00
movedTo, alsoKnownAs,
pinnedContent, accessKeys,
2021-05-12 18:31:41 +00:00
joinedDate, occupationName)
2020-11-09 22:44:03 +00:00
2021-02-05 15:30:51 +00:00
# keyboard navigation
2021-02-05 17:05:53 +00:00
userPathStr = '/users/' + nickname
2021-02-05 17:44:50 +00:00
deft = defaultTimeline
isGroup = False
followersStr = translate['Followers']
2021-12-25 16:17:53 +00:00
if isGroupAccount(base_dir, nickname, domain):
isGroup = True
followersStr = translate['Members']
2021-02-06 10:35:47 +00:00
menuTimeline = \
2021-02-06 10:46:03 +00:00
htmlHideFromScreenReader('🏠') + ' ' + \
translate['Switch to timeline view']
2021-02-06 10:35:47 +00:00
menuEdit = \
2021-02-06 10:46:03 +00:00
htmlHideFromScreenReader('') + ' ' + translate['Edit']
if not isGroup:
menuFollowing = \
htmlHideFromScreenReader('👥') + ' ' + translate['Following']
2021-02-06 10:35:47 +00:00
menuFollowers = \
2021-09-21 17:20:45 +00:00
htmlHideFromScreenReader('👪') + ' ' + followersStr
if not isGroup:
menuRoles = \
htmlHideFromScreenReader('🤚') + ' ' + translate['Roles']
menuSkills = \
htmlHideFromScreenReader('🛠') + ' ' + translate['Skills']
2021-02-06 10:35:47 +00:00
menuLogout = \
2021-02-06 10:46:03 +00:00
htmlHideFromScreenReader('') + ' ' + translate['Logout']
2021-02-05 17:05:53 +00:00
navLinks = {
2021-02-06 10:35:47 +00:00
menuTimeline: userPathStr + '/' + deft,
menuEdit: userPathStr + '/editprofile',
menuFollowing: userPathStr + '/following#timeline',
menuFollowers: userPathStr + '/followers#timeline',
menuRoles: userPathStr + '/roles#timeline',
menuSkills: userPathStr + '/skills#timeline',
menuLogout: '/logout'
2021-02-05 17:55:49 +00:00
}
2021-12-25 16:17:53 +00:00
if isArtist(base_dir, nickname):
2021-12-04 16:59:50 +00:00
menuThemeDesigner = \
htmlHideFromScreenReader('🎨') + ' ' + translate['Theme Designer']
navLinks[menuThemeDesigner] = userPathStr + '/themedesigner'
2021-04-22 14:12:59 +00:00
navAccessKeys = {}
for variableName, key in accessKeys.items():
if not locals().get(variableName):
continue
navAccessKeys[locals()[variableName]] = key
2021-12-25 23:09:49 +00:00
profileStr = htmlKeyboardNavigation(text_mode_banner,
2021-04-22 11:57:35 +00:00
navLinks, navAccessKeys)
profileStr += profileHeaderStr + donateSection
profileStr += '<div class="container" id="buttonheader">\n'
profileStr += ' <center>'
profileStr += \
' <a href="' + usersPath + '#buttonheader"><button class="' + \
postsButton + '"><span>' + translate['Posts'] + \
' </span></button></a>'
if not isGroup:
profileStr += \
' <a href="' + usersPath + '/following#buttonheader">' + \
'<button class="' + followingButton + '"><span>' + \
translate['Following'] + ' </span></button></a>'
profileStr += \
' <a href="' + usersPath + '/followers#buttonheader">' + \
'<button class="' + followersButton + \
2021-09-21 17:20:45 +00:00
'"><span>' + followersStr + ' </span></button></a>'
if not isGroup:
profileStr += \
' <a href="' + usersPath + '/roles#buttonheader">' + \
'<button class="' + rolesButton + '"><span>' + \
translate['Roles'] + \
' </span></button></a>'
profileStr += \
' <a href="' + usersPath + '/skills#buttonheader">' + \
'<button class="' + skillsButton + '"><span>' + \
translate['Skills'] + ' </span></button></a>'
# profileStr += \
# ' <a href="' + usersPath + '/shares#buttonheader">' + \
# '<button class="' + sharesButton + '"><span>' + \
# translate['Shares'] + ' </span></button></a>'
# profileStr += \
# ' <a href="' + usersPath + '/wanted#buttonheader">' + \
# '<button class="' + wantedButton + '"><span>' + \
# translate['Wanted'] + ' </span></button></a>'
profileStr += logoutStr + editProfileStr
profileStr += ' </center>'
profileStr += '</div>'
2020-11-09 22:44:03 +00:00
2021-02-05 17:55:49 +00:00
# start of #timeline
profileStr += '<div id="timeline">\n'
2020-11-09 22:44:03 +00:00
profileStr += followApprovalsSection
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
licenseStr = \
'<a href="https://gitlab.com/bashrc2/epicyon">' + \
'<img loading="lazy" class="license" alt="' + \
translate['Get the source code'] + '" title="' + \
translate['Get the source code'] + '" src="/icons/agpl.png" /></a>'
if selected == 'posts':
2020-11-27 18:50:54 +00:00
profileStr += \
2021-12-25 20:28:06 +00:00
_htmlProfilePosts(recentPostsCache, max_recent_posts,
translate,
2021-12-25 17:09:22 +00:00
base_dir, http_prefix, authorized,
nickname, domain, port,
2021-12-25 22:28:18 +00:00
session, cached_webfingers, person_cache,
2021-12-25 20:34:38 +00:00
project_version,
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:03:28 +00:00
theme, system_language,
2021-12-25 18:23:12 +00:00
max_like_count,
2021-12-25 23:03:28 +00:00
signing_priv_key_pem,
2021-12-25 23:26:38 +00:00
cw_lists, lists_enabled) + licenseStr
if not isGroup:
if selected == 'following':
profileStr += \
2021-12-25 17:09:22 +00:00
_htmlProfileFollowing(translate, base_dir, http_prefix,
authorized, nickname,
domain, port, session,
2021-12-25 22:28:18 +00:00
cached_webfingers,
2021-12-25 22:17:49 +00:00
person_cache, extraJson,
2021-12-25 20:34:38 +00:00
project_version, ["unfollow"], selected,
usersPath, pageNumber, maxItemsPerPage,
2021-12-25 18:47:04 +00:00
dormant_months, debug,
2021-12-25 23:03:28 +00:00
signing_priv_key_pem)
if selected == 'followers':
profileStr += \
2021-12-25 17:09:22 +00:00
_htmlProfileFollowing(translate, base_dir, http_prefix,
authorized, nickname,
domain, port, session,
2021-12-25 22:28:18 +00:00
cached_webfingers,
2021-12-25 22:17:49 +00:00
person_cache, extraJson,
2021-12-25 20:34:38 +00:00
project_version, ["block"],
selected, usersPath, pageNumber,
2021-12-25 18:47:04 +00:00
maxItemsPerPage, dormant_months, debug,
2021-12-25 23:03:28 +00:00
signing_priv_key_pem)
if not isGroup:
if selected == 'roles':
profileStr += \
2021-12-26 10:00:46 +00:00
_htmlProfileRoles(translate, nickname, domain_full,
extraJson)
2021-09-21 17:47:25 +00:00
elif selected == 'skills':
profileStr += \
2021-12-26 10:00:46 +00:00
_htmlProfileSkills(translate, nickname, domain_full, extraJson)
# elif selected == 'shares':
# profileStr += \
# _htmlProfileShares(actor, translate,
2021-12-26 10:00:46 +00:00
# nickname, domain_full,
# extraJson, 'shares') + licenseStr
# elif selected == 'wanted':
# profileStr += \
# _htmlProfileShares(actor, translate,
2021-12-26 10:00:46 +00:00
# nickname, domain_full,
# extraJson, 'wanted') + licenseStr
2021-02-05 17:55:49 +00:00
# end of #timeline
profileStr += '</div>'
2020-11-12 17:05:38 +00:00
2021-01-11 19:46:21 +00:00
instanceTitle = \
2021-12-25 16:17:53 +00:00
getConfigParam(base_dir, 'instanceTitle')
2020-11-12 17:05:38 +00:00
profileStr = \
htmlHeaderWithPersonMarkup(cssFilename, instanceTitle,
2021-12-26 10:08:06 +00:00
profile_json, city,
2021-12-25 17:13:38 +00:00
content_license_url) + \
profileStr + htmlFooter()
2020-11-09 22:44:03 +00:00
return profileStr
2021-12-25 20:28:06 +00:00
def _htmlProfilePosts(recentPostsCache: {}, max_recent_posts: int,
translate: {},
2021-12-25 17:09:22 +00:00
base_dir: str, http_prefix: str,
authorized: bool,
nickname: str, domain: str, port: int,
2021-12-25 22:28:18 +00:00
session, cached_webfingers: {}, person_cache: {},
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 18:23:12 +00:00
max_like_count: int,
2021-12-25 23:03:28 +00:00
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
"""Shows posts on the profile screen
These should only be public posts
"""
2021-12-25 16:17:53 +00:00
separatorStr = htmlPostSeparator(base_dir, None)
2020-11-09 22:44:03 +00:00
profileStr = ''
maxItems = 4
ctr = 0
currPage = 1
2020-11-27 12:29:20 +00:00
boxName = 'outbox'
2020-11-09 22:44:03 +00:00
while ctr < maxItems and currPage < 4:
outboxFeedPathStr = \
'/users/' + nickname + '/' + boxName + '?page=' + \
str(currPage)
2020-11-09 22:44:03 +00:00
outboxFeed = \
2021-12-25 16:17:53 +00:00
personBoxJson({}, session, base_dir, domain,
2020-11-09 22:44:03 +00:00
port,
outboxFeedPathStr,
2021-12-25 17:09:22 +00:00
http_prefix,
2020-11-27 12:29:20 +00:00
10, boxName,
2020-11-09 22:44:03 +00:00
authorized, 0, False, 0)
if not outboxFeed:
2020-11-27 16:14:54 +00:00
break
2020-11-09 22:44:03 +00:00
if len(outboxFeed['orderedItems']) == 0:
break
for item in outboxFeed['orderedItems']:
if item['type'] == 'Create':
postStr = \
2021-12-25 23:03:28 +00:00
individualPostAsHtml(signing_priv_key_pem,
True, recentPostsCache,
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,
False, False, False,
2021-10-21 13:08:21 +00:00
True, False, False,
2021-12-25 23:26:38 +00:00
cw_lists, lists_enabled)
2020-11-09 22:44:03 +00:00
if postStr:
2020-11-18 21:18:51 +00:00
profileStr += postStr + separatorStr
2020-11-09 22:44:03 +00:00
ctr += 1
if ctr >= maxItems:
break
currPage += 1
return profileStr
2021-12-25 17:09:22 +00:00
def _htmlProfileFollowing(translate: {}, base_dir: str, http_prefix: str,
authorized: bool,
nickname: str, domain: str, port: int,
2021-12-25 22:28:18 +00:00
session, cached_webfingers: {}, person_cache: {},
2021-12-25 20:34:38 +00:00
followingJson: {}, project_version: str,
buttons: [],
feedName: str, actor: str,
pageNumber: int,
maxItemsPerPage: int,
2021-12-25 18:47:04 +00:00
dormant_months: int, debug: bool,
2021-12-25 23:03:28 +00:00
signing_priv_key_pem: str) -> str:
2020-11-09 22:44:03 +00:00
"""Shows following on the profile screen
"""
profileStr = ''
if authorized and pageNumber:
if authorized and pageNumber > 1:
# page up arrow
profileStr += \
' <center>\n' + \
' <a href="' + actor + '/' + feedName + \
2020-11-24 14:49:07 +00:00
'?page=' + str(pageNumber - 1) + '#buttonheader' + \
2020-11-09 22:44:03 +00:00
'"><img loading="lazy" class="pageicon" src="/' + \
2020-12-09 13:08:26 +00:00
'icons/pageup.png" title="' + \
2020-11-09 22:44:03 +00:00
translate['Page up'] + '" alt="' + \
translate['Page up'] + '"></a>\n' + \
' </center>\n'
for followingActor in followingJson['orderedItems']:
2020-12-13 12:48:04 +00:00
# is this a dormant followed account?
dormant = False
2020-12-13 12:45:29 +00:00
if authorized and feedName == 'following':
dormant = \
2021-12-25 16:17:53 +00:00
isDormant(base_dir, nickname, domain, followingActor,
2021-12-25 18:47:04 +00:00
dormant_months)
2020-12-13 12:48:04 +00:00
2020-11-09 22:44:03 +00:00
profileStr += \
2021-12-25 23:03:28 +00:00
_individualFollowAsHtml(signing_priv_key_pem,
2021-12-25 16:17:53 +00:00
translate, base_dir, session,
2021-12-25 22:28:18 +00:00
cached_webfingers, person_cache,
domain, followingActor,
authorized, nickname,
2021-12-25 20:34:38 +00:00
http_prefix, project_version, dormant,
2021-03-14 19:22:58 +00:00
debug, buttons)
2020-12-13 12:48:04 +00:00
2020-11-09 22:44:03 +00:00
if authorized and maxItemsPerPage and pageNumber:
if len(followingJson['orderedItems']) >= maxItemsPerPage:
# page down arrow
profileStr += \
' <center>\n' + \
' <a href="' + actor + '/' + feedName + \
2020-11-24 14:49:07 +00:00
'?page=' + str(pageNumber + 1) + '#buttonheader' + \
2020-11-09 22:44:03 +00:00
'"><img loading="lazy" class="pageicon" src="/' + \
2020-12-09 13:08:26 +00:00
'icons/pagedown.png" title="' + \
2020-11-09 22:44:03 +00:00
translate['Page down'] + '" alt="' + \
translate['Page down'] + '"></a>\n' + \
' </center>\n'
2020-11-09 22:44:03 +00:00
return profileStr
def _htmlProfileRoles(translate: {}, nickname: str, domain: str,
rolesList: []) -> str:
2020-11-09 22:44:03 +00:00
"""Shows roles on the profile screen
"""
profileStr = ''
profileStr += \
'<div class="roles">\n<div class="roles-inner">\n'
for role in rolesList:
if translate.get(role):
profileStr += '<h3>' + translate[role] + '</h3>\n'
else:
profileStr += '<h3>' + role + '</h3>\n'
profileStr += '</div></div>\n'
2020-11-09 22:44:03 +00:00
if len(profileStr) == 0:
profileStr += \
'<p>@' + nickname + '@' + domain + ' has no roles assigned</p>\n'
else:
profileStr = '<div>' + profileStr + '</div>\n'
return profileStr
def _htmlProfileSkills(translate: {}, nickname: str, domain: str,
skillsJson: {}) -> str:
2020-11-09 22:44:03 +00:00
"""Shows skills on the profile screen
"""
profileStr = ''
for skill, level in skillsJson.items():
profileStr += \
'<div>' + skill + \
'<br><div id="myProgress"><div id="myBar" style="width:' + \
str(level) + '%"></div></div></div>\n<br>\n'
if len(profileStr) > 0:
profileStr = '<center><div class="skill-title">' + \
profileStr + '</div></center>\n'
return profileStr
def _htmlProfileShares(actor: str, translate: {},
2021-08-09 20:05:14 +00:00
nickname: str, domain: str, sharesJson: {},
sharesFileType: str) -> str:
2020-11-09 22:44:03 +00:00
"""Shows shares on the profile screen
"""
profileStr = ''
for item in sharesJson['orderedItems']:
profileStr += htmlIndividualShare(domain, item['shareId'],
2021-08-09 20:05:14 +00:00
actor, item, translate, False, False,
sharesFileType)
2020-11-09 22:44:03 +00:00
if len(profileStr) > 0:
profileStr = '<div class="share-title">' + profileStr + '</div>\n'
return profileStr
2021-12-25 16:17:53 +00:00
def _grayscaleEnabled(base_dir: str) -> bool:
2021-06-27 17:59:46 +00:00
"""Is grayscale UI enabled?
"""
2021-12-25 16:17:53 +00:00
return os.path.isfile(base_dir + '/accounts/.grayscale')
2021-06-27 17:59:46 +00:00
2021-12-25 16:17:53 +00:00
def _htmlThemesDropdown(base_dir: str, translate: {}) -> str:
2021-06-27 17:59:46 +00:00
"""Returns the html for theme selection dropdown
"""
# Themes section
2021-12-25 16:17:53 +00:00
themes = getThemesList(base_dir)
2021-06-27 17:59:46 +00:00
themesDropdown = ' <label class="labels">' + \
translate['Theme'] + '</label><br>\n'
2021-12-25 16:17:53 +00:00
grayscale = _grayscaleEnabled(base_dir)
2021-06-27 17:59:46 +00:00
themesDropdown += \
2021-07-22 16:58:59 +00:00
editCheckBox(translate['Grayscale'], 'grayscale', grayscale)
2021-06-27 17:59:46 +00:00
themesDropdown += ' <select id="themeDropdown" ' + \
'name="themeDropdown" class="theme">'
2021-12-25 23:35:50 +00:00
for theme_name in themes:
translatedThemeName = theme_name
if translate.get(theme_name):
translatedThemeName = translate[theme_name]
2021-06-27 17:59:46 +00:00
themesDropdown += ' <option value="' + \
2021-12-25 23:35:50 +00:00
theme_name.lower() + '">' + \
2021-06-27 17:59:46 +00:00
translatedThemeName + '</option>'
themesDropdown += ' </select><br>'
2021-12-25 16:17:53 +00:00
if os.path.isfile(base_dir + '/fonts/custom.woff') or \
os.path.isfile(base_dir + '/fonts/custom.woff2') or \
os.path.isfile(base_dir + '/fonts/custom.otf') or \
os.path.isfile(base_dir + '/fonts/custom.ttf'):
2021-06-27 17:59:46 +00:00
themesDropdown += \
2021-07-22 16:58:59 +00:00
editCheckBox(translate['Remove the custom font'],
'removeCustomFont', False)
2021-12-25 23:35:50 +00:00
theme_name = getConfigParam(base_dir, 'theme')
2021-06-27 17:59:46 +00:00
themesDropdown = \
2021-12-25 23:35:50 +00:00
themesDropdown.replace('<option value="' + theme_name + '">',
'<option value="' + theme_name +
2021-06-27 17:59:46 +00:00
'" selected>')
return themesDropdown
2021-12-25 16:17:53 +00:00
def _htmlEditProfileGraphicDesign(base_dir: str, translate: {}) -> str:
"""Graphic design section on Edit Profile screen
"""
themeFormats = '.zip, .gz'
2021-07-22 16:58:59 +00:00
graphicsStr = beginEditSection(translate['Graphic Design'])
2021-12-25 18:20:56 +00:00
low_bandwidth = getConfigParam(base_dir, 'low_bandwidth')
if not low_bandwidth:
low_bandwidth = False
2021-12-25 16:17:53 +00:00
graphicsStr += _htmlThemesDropdown(base_dir, translate)
graphicsStr += \
' <label class="labels">' + \
translate['Import Theme'] + '</label>\n'
graphicsStr += ' <input type="file" id="importTheme" '
graphicsStr += 'name="submitImportTheme" '
graphicsStr += 'accept="' + themeFormats + '">\n'
graphicsStr += \
' <label class="labels">' + \
translate['Export Theme'] + '</label><br>\n'
graphicsStr += \
' <button type="submit" class="button" ' + \
2021-08-13 17:35:31 +00:00
'name="submitExportTheme">➤</button><br>\n'
2021-08-13 17:34:23 +00:00
graphicsStr += \
2021-12-25 18:20:56 +00:00
editCheckBox(translate['Low Bandwidth'], 'low_bandwidth',
bool(low_bandwidth))
2021-07-22 16:58:59 +00:00
graphicsStr += endEditSection()
return graphicsStr
2021-12-25 16:17:53 +00:00
def _htmlEditProfileTwitter(base_dir: str, translate: {},
2021-09-18 17:08:14 +00:00
removeTwitter: str) -> str:
"""Edit twitter settings within profile
"""
# Twitter section
twitterStr = beginEditSection(translate['Twitter'])
twitterStr += \
editCheckBox(translate['Remove Twitter posts'],
'removeTwitter', removeTwitter)
2021-12-25 20:55:47 +00:00
twitter_replacement_domain = getConfigParam(base_dir, "twitterdomain")
if not twitter_replacement_domain:
twitter_replacement_domain = ''
2021-09-18 17:08:14 +00:00
twitterStr += \
editTextField(translate['Twitter Replacement Domain'],
2021-12-25 20:55:47 +00:00
'twitterdomain', twitter_replacement_domain)
2021-09-18 17:08:14 +00:00
twitterStr += endEditSection()
return twitterStr
2021-12-25 16:17:53 +00:00
def _htmlEditProfileInstance(base_dir: str, translate: {},
2021-12-25 23:38:53 +00:00
peertube_instances: [],
2021-12-25 20:25:07 +00:00
media_instanceStr: str,
2021-12-25 20:22:25 +00:00
blogs_instanceStr: str,
2021-12-25 20:20:08 +00:00
news_instanceStr: str) -> (str, str, str, str):
"""Edit profile instance settings
"""
imageFormats = getImageFormats()
# Instance details section
instanceDescription = \
2021-12-25 16:17:53 +00:00
getConfigParam(base_dir, 'instanceDescription')
customSubmitText = \
2021-12-25 16:17:53 +00:00
getConfigParam(base_dir, 'customSubmitText')
instanceDescriptionShort = \
2021-12-25 16:17:53 +00:00
getConfigParam(base_dir, 'instanceDescriptionShort')
instanceTitle = \
2021-12-25 16:17:53 +00:00
getConfigParam(base_dir, 'instanceTitle')
2021-12-25 17:13:38 +00:00
content_license_url = \
getConfigParam(base_dir, 'content_license_url')
if not content_license_url:
content_license_url = 'https://creativecommons.org/licenses/by/4.0'
2021-07-22 16:58:59 +00:00
instanceStr = beginEditSection(translate['Instance Settings'])
2021-07-22 12:36:31 +00:00
instanceStr += \
2021-07-22 16:58:59 +00:00
editTextField(translate['Instance Title'],
'instanceTitle', instanceTitle)
2021-07-22 12:36:31 +00:00
instanceStr += '<br>\n'
instanceStr += \
2021-07-22 16:58:59 +00:00
editTextField(translate['Instance Short Description'],
'instanceDescriptionShort', instanceDescriptionShort)
2021-07-22 12:36:31 +00:00
instanceStr += '<br>\n'
instanceStr += \
2021-07-22 16:58:59 +00:00
editTextArea(translate['Instance Description'],
'instanceDescription', instanceDescription, 200,
'', True)
2021-11-08 20:21:54 +00:00
instanceStr += \
editTextField(translate['Content License'],
2021-12-25 17:13:38 +00:00
'content_license_url', content_license_url)
2021-11-08 20:21:54 +00:00
instanceStr += '<br>\n'
instanceStr += \
2021-07-22 16:58:59 +00:00
editTextField(translate['Custom post submit button text'],
'customSubmitText', customSubmitText)
2021-07-22 12:36:31 +00:00
instanceStr += '<br>\n'
instanceStr += \
' <label class="labels">' + \
2021-07-22 10:22:01 +00:00
translate['Instance Logo'] + '</label>' + \
' <input type="file" id="instanceLogo" name="instanceLogo"' + \
' accept="' + imageFormats + '"><br>\n' + \
' <br><label class="labels">' + \
translate['Security'] + '</label><br>\n'
nodeInfoStr = \
translate['Show numbers of accounts within instance metadata']
2021-12-25 18:32:17 +00:00
if getConfigParam(base_dir, "show_node_info_accounts"):
instanceStr += \
2021-12-25 18:32:17 +00:00
editCheckBox(nodeInfoStr, 'show_node_info_accounts', True)
else:
instanceStr += \
2021-12-25 18:32:17 +00:00
editCheckBox(nodeInfoStr, 'show_node_info_accounts', False)
nodeInfoStr = \
translate['Show version number within instance metadata']
2021-12-25 18:35:24 +00:00
if getConfigParam(base_dir, "show_node_info_version"):
instanceStr += \
2021-12-25 18:35:24 +00:00
editCheckBox(nodeInfoStr, 'show_node_info_version', True)
else:
instanceStr += \
2021-12-25 18:35:24 +00:00
editCheckBox(nodeInfoStr, 'show_node_info_version', False)
2021-12-25 18:40:32 +00:00
if getConfigParam(base_dir, "verify_all_signatures"):
instanceStr += \
2021-07-22 16:58:59 +00:00
editCheckBox(translate['Verify all signatures'],
'verifyallsignatures', True)
else:
instanceStr += \
2021-07-22 16:58:59 +00:00
editCheckBox(translate['Verify all signatures'],
'verifyallsignatures', False)
instanceStr += translate['Enabling broch mode'] + '<br>\n'
2021-12-25 18:38:19 +00:00
if getConfigParam(base_dir, "broch_mode"):
instanceStr += \
2021-12-25 18:38:19 +00:00
editCheckBox(translate['Broch mode'], 'broch_mode', True)
else:
instanceStr += \
2021-12-25 18:38:19 +00:00
editCheckBox(translate['Broch mode'], 'broch_mode', False)
# Instance type
instanceStr += \
' <br><label class="labels">' + \
2021-07-22 12:36:31 +00:00
translate['Type of instance'] + '</label><br>\n'
instanceStr += \
2021-07-22 16:58:59 +00:00
editCheckBox(translate['This is a media instance'],
2021-12-25 20:25:07 +00:00
'media_instance', media_instanceStr)
2021-07-22 12:36:31 +00:00
instanceStr += \
2021-07-22 16:58:59 +00:00
editCheckBox(translate['This is a blogging instance'],
2021-12-25 20:22:25 +00:00
'blogs_instance', blogs_instanceStr)
2021-07-22 12:36:31 +00:00
instanceStr += \
2021-07-22 16:58:59 +00:00
editCheckBox(translate['This is a news instance'],
2021-12-25 20:20:08 +00:00
'news_instance', news_instanceStr)
2021-07-22 12:36:31 +00:00
2021-07-22 16:58:59 +00:00
instanceStr += endEditSection()
# Role assignments section
moderators = ''
2021-12-25 16:17:53 +00:00
moderatorsFile = base_dir + '/accounts/moderators.txt'
if os.path.isfile(moderatorsFile):
2021-07-13 14:40:49 +00:00
with open(moderatorsFile, 'r') as f:
moderators = f.read()
# site moderators
2021-07-22 10:22:01 +00:00
roleAssignStr = \
2021-07-22 16:58:59 +00:00
beginEditSection(translate['Role Assignment']) + \
2021-07-22 10:22:01 +00:00
' <b><label class="labels">' + \
translate['Moderators'] + '</label></b><br>\n' + \
' ' + \
translate['A list of moderator nicknames. One per line.'] + \
' <textarea id="message" name="moderators" placeholder="' + \
translate['List of moderator nicknames'] + \
'..." style="height:200px" spellcheck="false">' + \
moderators + '</textarea>'
# site editors
editors = ''
2021-12-25 16:17:53 +00:00
editorsFile = base_dir + '/accounts/editors.txt'
if os.path.isfile(editorsFile):
2021-07-13 14:40:49 +00:00
with open(editorsFile, 'r') as f:
editors = f.read()
roleAssignStr += \
2021-07-22 10:22:01 +00:00
' <b><label class="labels">' + \
translate['Site Editors'] + '</label></b><br>\n' + \
' ' + \
translate['A list of editor nicknames. One per line.'] + \
' <textarea id="message" name="editors" placeholder="" ' + \
'style="height:200px" spellcheck="false">' + \
editors + '</textarea>'
# counselors
counselors = ''
2021-12-25 16:17:53 +00:00
counselorsFile = base_dir + '/accounts/counselors.txt'
if os.path.isfile(counselorsFile):
2021-07-13 14:40:49 +00:00
with open(counselorsFile, 'r') as f:
counselors = f.read()
roleAssignStr += \
2021-07-22 18:35:45 +00:00
editTextArea(translate['Counselors'], 'counselors', counselors,
200, '', False)
# artists
artists = ''
2021-12-25 16:17:53 +00:00
artistsFile = base_dir + '/accounts/artists.txt'
if os.path.isfile(artistsFile):
2021-07-13 14:40:49 +00:00
with open(artistsFile, 'r') as f:
artists = f.read()
roleAssignStr += \
2021-07-22 18:35:45 +00:00
editTextArea(translate['Artists'], 'artists', artists,
200, '', False)
roleAssignStr += endEditSection()
# Video section
2021-07-22 18:35:45 +00:00
peertubeStr = beginEditSection(translate['Video Settings'])
2021-12-25 23:38:53 +00:00
peertube_instancesStr = ''
for url in peertube_instances:
peertube_instancesStr += url + '\n'
peertubeStr += \
2021-07-22 18:35:45 +00:00
editTextArea(translate['Peertube Instances'], 'ptInstances',
2021-12-25 23:38:53 +00:00
peertube_instancesStr, 200, '', False)
2021-07-22 18:35:45 +00:00
peertubeStr += \
2021-07-22 12:36:31 +00:00
' <br>\n'
2021-12-25 17:15:52 +00:00
yt_replace_domain = getConfigParam(base_dir, "youtubedomain")
if not yt_replace_domain:
yt_replace_domain = ''
peertubeStr += \
2021-07-22 16:58:59 +00:00
editTextField(translate['YouTube Replacement Domain'],
2021-12-25 17:15:52 +00:00
'ytdomain', yt_replace_domain)
2021-07-22 16:58:59 +00:00
peertubeStr += endEditSection()
2021-12-25 16:17:53 +00:00
libretranslateUrl = getConfigParam(base_dir, 'libretranslateUrl')
libretranslateApiKey = getConfigParam(base_dir, 'libretranslateApiKey')
libretranslateStr = \
_htmlEditProfileLibreTranslate(translate,
libretranslateUrl,
libretranslateApiKey)
return instanceStr, roleAssignStr, peertubeStr, libretranslateStr
def _htmlEditProfileDangerZone(translate: {}) -> str:
"""danger zone section of Edit Profile screen
"""
2021-07-22 16:58:59 +00:00
editProfileForm = beginEditSection(translate['Danger Zone'])
2021-07-22 12:36:31 +00:00
editProfileForm += \
2021-07-22 10:22:01 +00:00
' <b><label class="labels">' + \
2021-07-22 12:36:31 +00:00
translate['Danger Zone'] + '</label></b><br>\n'
editProfileForm += \
2021-07-22 16:58:59 +00:00
editCheckBox(translate['Deactivate this account'],
'deactivateThisAccount', False)
2021-07-22 12:36:31 +00:00
2021-07-22 16:58:59 +00:00
editProfileForm += endEditSection()
return editProfileForm
2021-10-20 10:41:58 +00:00
def _htmlSystemMonitor(nickname: str, translate: {}) -> str:
"""Links to performance graphs
"""
systemMonitorStr = beginEditSection(translate['System Monitor'])
2021-10-20 10:41:58 +00:00
systemMonitorStr += '<p><a href="/users/' + nickname + \
'/performance?graph=get">📊 GET</a></p>'
systemMonitorStr += '<p><a href="/users/' + nickname + \
'/performance?graph=post">📊 POST</a></p>'
systemMonitorStr += endEditSection()
return systemMonitorStr
2021-12-25 16:17:53 +00:00
def _htmlEditProfileSkills(base_dir: str, nickname: str, domain: str,
translate: {}) -> str:
"""skills section of Edit Profile screen
"""
2021-12-25 16:17:53 +00:00
skills = getSkills(base_dir, nickname, domain)
skillsStr = ''
skillCtr = 1
if skills:
for skillDesc, skillValue in skills.items():
2021-12-25 16:17:53 +00:00
if isFiltered(base_dir, nickname, domain, skillDesc):
continue
skillsStr += \
'<p><input type="text" placeholder="' + translate['Skill'] + \
' ' + str(skillCtr) + '" name="skillName' + str(skillCtr) + \
2021-07-22 10:22:01 +00:00
'" value="' + skillDesc + '" style="width:40%">' + \
'<input type="range" min="1" max="100" ' + \
'class="slider" name="skillValue' + \
str(skillCtr) + '" value="' + str(skillValue) + '"></p>'
skillCtr += 1
skillsStr += \
'<p><input type="text" placeholder="Skill ' + str(skillCtr) + \
'" name="skillName' + str(skillCtr) + \
2021-07-22 10:22:01 +00:00
'" value="" style="width:40%">' + \
'<input type="range" min="1" max="100" ' + \
'class="slider" name="skillValue' + \
2021-07-22 16:58:59 +00:00
str(skillCtr) + '" value="50"></p>' + endEditSection()
idx = 'If you want to participate within organizations then you ' + \
'can indicate some skills that you have and approximate ' + \
'proficiency levels. This helps organizers to construct ' + \
'teams with an appropriate combination of skills.'
2021-07-22 10:22:01 +00:00
editProfileForm = \
2021-07-22 16:58:59 +00:00
beginEditSection(translate['Skills']) + \
2021-07-22 10:22:01 +00:00
' <b><label class="labels">' + \
translate['Skills'] + '</label></b><br>\n' + \
' <label class="labels">' + \
translate[idx] + '</label>\n' + skillsStr
return editProfileForm
2021-12-25 16:17:53 +00:00
def _htmlEditProfileGitProjects(base_dir: str, nickname: str, domain: str,
translate: {}) -> str:
"""git projects section of edit profile screen
"""
gitProjectsStr = ''
gitProjectsFilename = \
2021-12-25 16:17:53 +00:00
acctDir(base_dir, nickname, domain) + '/gitprojects.txt'
if os.path.isfile(gitProjectsFilename):
with open(gitProjectsFilename, 'r') as gitProjectsFile:
gitProjectsStr = gitProjectsFile.read()
2021-07-22 18:35:45 +00:00
editProfileForm = beginEditSection(translate['Git Projects'])
2021-07-22 19:00:37 +00:00
idx = 'List of project names that you wish to receive git patches for'
editProfileForm += \
2021-07-22 18:35:45 +00:00
editTextArea(translate[idx], 'gitProjects', gitProjectsStr,
100, '', False)
2021-07-22 19:00:37 +00:00
editProfileForm += endEditSection()
return editProfileForm
2021-12-25 16:17:53 +00:00
def _htmlEditProfileSharedItems(base_dir: str, nickname: str, domain: str,
translate: {}) -> str:
"""shared items section of edit profile screen
"""
sharedItemsStr = ''
2021-12-25 18:05:01 +00:00
shared_items_federated_domainsStr = \
getConfigParam(base_dir, 'shared_items_federated_domains')
if shared_items_federated_domainsStr:
shared_items_federated_domainsList = \
shared_items_federated_domainsStr.split(',')
for sharedFederatedDomain in shared_items_federated_domainsList:
sharedItemsStr += sharedFederatedDomain.strip() + '\n'
editProfileForm = beginEditSection(translate['Shares'])
idx = 'List of domains which can access the shared items catalog'
editProfileForm += \
editTextArea(translate[idx], 'shareDomainList',
sharedItemsStr, 200, '', False)
editProfileForm += endEditSection()
return editProfileForm
2021-12-25 16:17:53 +00:00
def _htmlEditProfileFiltering(base_dir: str, nickname: str, domain: str,
2021-12-25 18:27:11 +00:00
user_agents_blocked: str,
2021-10-24 09:57:10 +00:00
translate: {}, replyIntervalHours: int,
2021-12-25 23:26:38 +00:00
cw_lists: {}, lists_enabled: str) -> str:
"""Filtering and blocking section of edit profile screen
"""
filterStr = ''
filterFilename = \
2021-12-25 16:17:53 +00:00
acctDir(base_dir, nickname, domain) + '/filters.txt'
if os.path.isfile(filterFilename):
with open(filterFilename, 'r') as filterfile:
filterStr = filterfile.read()
2021-12-14 14:27:56 +00:00
filterBioStr = ''
filterBioFilename = \
2021-12-25 16:17:53 +00:00
acctDir(base_dir, nickname, domain) + '/filters_bio.txt'
2021-12-14 14:27:56 +00:00
if os.path.isfile(filterBioFilename):
with open(filterBioFilename, 'r') as filterfile:
filterBioStr = filterfile.read()
switchStr = ''
switchFilename = \
2021-12-25 16:17:53 +00:00
acctDir(base_dir, nickname, domain) + '/replacewords.txt'
if os.path.isfile(switchFilename):
with open(switchFilename, 'r') as switchfile:
switchStr = switchfile.read()
autoTags = ''
autoTagsFilename = \
2021-12-25 16:17:53 +00:00
acctDir(base_dir, nickname, domain) + '/autotags.txt'
if os.path.isfile(autoTagsFilename):
with open(autoTagsFilename, 'r') as autoTagsFile:
autoTags = autoTagsFile.read()
autoCW = ''
autoCWFilename = \
2021-12-25 16:17:53 +00:00
acctDir(base_dir, nickname, domain) + '/autocw.txt'
if os.path.isfile(autoCWFilename):
with open(autoCWFilename, 'r') as autoCWFile:
autoCW = autoCWFile.read()
blockedStr = ''
blockedFilename = \
2021-12-25 16:17:53 +00:00
acctDir(base_dir, nickname, domain) + '/blocking.txt'
if os.path.isfile(blockedFilename):
with open(blockedFilename, 'r') as blockedfile:
blockedStr = blockedfile.read()
dmAllowedInstancesStr = ''
dmAllowedInstancesFilename = \
2021-12-25 16:17:53 +00:00
acctDir(base_dir, nickname, domain) + '/dmAllowedInstances.txt'
if os.path.isfile(dmAllowedInstancesFilename):
with open(dmAllowedInstancesFilename, 'r') as dmAllowedInstancesFile:
dmAllowedInstancesStr = dmAllowedInstancesFile.read()
allowedInstancesStr = ''
allowedInstancesFilename = \
2021-12-25 16:17:53 +00:00
acctDir(base_dir, nickname, domain) + '/allowedinstances.txt'
if os.path.isfile(allowedInstancesFilename):
with open(allowedInstancesFilename, 'r') as allowedInstancesFile:
allowedInstancesStr = allowedInstancesFile.read()
2021-07-22 16:58:59 +00:00
editProfileForm = beginEditSection(translate['Filtering and Blocking'])
idx = 'Hours after posting during which replies are allowed'
editProfileForm += \
' <label class="labels">' + \
translate[idx] + \
':</label> <input type="number" name="replyhours" ' + \
'min="0" max="999999999999" step="1" ' + \
'value="' + str(replyIntervalHours) + '"><br>\n'
editProfileForm += \
'<label class="labels">' + \
translate['City for spoofed GPS image metadata'] + \
'</label><br>\n'
city = ''
2021-12-25 16:17:53 +00:00
cityFilename = acctDir(base_dir, nickname, domain) + '/city.txt'
if os.path.isfile(cityFilename):
with open(cityFilename, 'r') as fp:
city = fp.read().replace('\n', '')
2021-12-25 16:17:53 +00:00
locationsFilename = base_dir + '/custom_locations.txt'
if not os.path.isfile(locationsFilename):
2021-12-25 16:17:53 +00:00
locationsFilename = base_dir + '/locations.txt'
cities = []
2021-07-13 14:40:49 +00:00
with open(locationsFilename, 'r') as f:
cities = f.readlines()
cities.sort()
editProfileForm += ' <select id="cityDropdown" ' + \
'name="cityDropdown" class="theme">\n'
city = city.lower()
for cityName in cities:
if ':' not in cityName:
continue
citySelected = ''
cityName = cityName.split(':')[0]
cityName = cityName.lower()
if city:
if city in cityName:
citySelected = ' selected'
editProfileForm += \
' <option value="' + cityName + \
'"' + citySelected.title() + '>' + \
cityName + '</option>\n'
editProfileForm += ' </select><br>\n'
editProfileForm += \
' <b><label class="labels">' + \
2021-07-22 10:22:01 +00:00
translate['Filtered words'] + '</label></b>\n' + \
' <br><label class="labels">' + \
translate['One per line'] + '</label>\n' + \
' <textarea id="message" ' + \
'name="filteredWords" style="height:200px" spellcheck="false">' + \
2021-07-22 10:22:01 +00:00
filterStr + '</textarea>\n' + \
' <br><b><label class="labels">' + \
translate['Filtered words within bio'] + '</label></b>\n' + \
' <br><label class="labels">' + \
translate['One per line'] + '</label>\n' + \
' <textarea id="message" ' + \
'name="filteredWordsBio" style="height:200px" spellcheck="false">' + \
2021-12-14 14:27:56 +00:00
filterBioStr + '</textarea>\n' + \
' <br><b><label class="labels">' + \
2021-07-22 10:22:01 +00:00
translate['Word Replacements'] + '</label></b>\n' + \
' <br><label class="labels">A -> B</label>\n' + \
' <textarea id="message" name="switchWords" ' + \
'style="height:200px" spellcheck="false">' + \
2021-07-22 10:22:01 +00:00
switchStr + '</textarea>\n' + \
' <br><b><label class="labels">' + \
2021-07-22 10:22:01 +00:00
translate['Autogenerated Hashtags'] + '</label></b>\n' + \
' <br><label class="labels">A -> #B</label>\n' + \
' <textarea id="message" name="autoTags" ' + \
'style="height:200px" spellcheck="false">' + \
2021-07-22 10:22:01 +00:00
autoTags + '</textarea>\n' + \
' <br><b><label class="labels">' + \
2021-07-22 10:22:01 +00:00
translate['Autogenerated Content Warnings'] + '</label></b>\n' + \
' <br><label class="labels">A -> B</label>\n' + \
' <textarea id="message" name="autoCW" ' + \
'style="height:200px" spellcheck="true">' + autoCW + '</textarea>\n'
idx = 'Blocked accounts, one per line, in the form ' + \
'nickname@domain or *@blockeddomain'
editProfileForm += \
2021-07-22 18:35:45 +00:00
editTextArea(translate['Blocked accounts'], 'blocked', blockedStr,
200, '', False)
idx = 'Direct messages are always allowed from these instances.'
editProfileForm += \
2021-07-22 18:35:45 +00:00
editTextArea(translate['Direct Message permitted instances'],
'dmAllowedInstances', dmAllowedInstancesStr,
200, '', False)
idx = 'Federate only with a defined set of instances. ' + \
'One domain name per line.'
editProfileForm += \
2021-07-22 10:22:01 +00:00
' <br><b><label class="labels">' + \
translate['Federation list'] + '</label></b>\n' + \
' <br><label class="labels">' + \
2021-07-22 10:22:01 +00:00
translate[idx] + '</label>\n' + \
' <textarea id="message" name="allowedInstances" ' + \
'style="height:200px" spellcheck="false">' + \
allowedInstancesStr + '</textarea>\n'
2021-12-25 16:17:53 +00:00
if isModerator(base_dir, nickname):
2021-10-24 09:57:10 +00:00
editProfileForm += \
'<a href="/users/' + nickname + '/crawlers">' + \
translate['Known Web Crawlers'] + '</a><br>\n'
2021-12-25 18:27:11 +00:00
user_agents_blockedStr = ''
for ua in user_agents_blocked:
if user_agents_blockedStr:
user_agents_blockedStr += '\n'
user_agents_blockedStr += ua
editProfileForm += \
2021-10-21 19:45:39 +00:00
editTextArea(translate['Blocked User Agents'],
2021-12-25 18:27:11 +00:00
'user_agents_blockedStr', user_agents_blockedStr,
2021-10-21 19:45:39 +00:00
200, '', False)
2021-12-25 23:26:38 +00:00
cw_listsStr = ''
for name, item in cw_lists.items():
2021-10-21 19:45:39 +00:00
variableName = getCWlistVariable(name)
listIsEnabled = False
2021-12-25 18:12:13 +00:00
if lists_enabled:
if name in lists_enabled:
2021-10-21 19:45:39 +00:00
listIsEnabled = True
if translate.get(name):
name = translate[name]
2021-12-25 23:26:38 +00:00
cw_listsStr += editCheckBox(name, variableName, listIsEnabled)
if cw_listsStr:
2021-10-21 19:45:39 +00:00
idx = 'Add content warnings for the following sites'
editProfileForm += \
'<label class="labels">' + translate[idx] + ':</label>\n' + \
2021-12-25 23:26:38 +00:00
'<br>' + cw_listsStr
2021-10-21 16:58:01 +00:00
2021-07-22 16:58:59 +00:00
editProfileForm += endEditSection()
return editProfileForm
def _htmlEditProfileChangePassword(translate: {}) -> str:
"""Change password section of edit profile screen
"""
2021-07-06 10:18:56 +00:00
editProfileForm = \
2021-07-22 16:58:59 +00:00
beginEditSection(translate['Change Password']) + \
'<label class="labels">' + translate['Change Password'] + \
2021-07-06 10:18:56 +00:00
'</label><br>\n' + \
2021-07-20 08:44:32 +00:00
' <input type="password" name="password" ' + \
2021-07-06 10:18:56 +00:00
'value=""><br>\n' + \
'<label class="labels">' + translate['Confirm Password'] + \
2021-07-06 10:18:56 +00:00
'</label><br>\n' + \
2021-07-20 08:44:32 +00:00
' <input type="password" name="passwordconfirm" value="">\n' + \
2021-07-22 16:58:59 +00:00
endEditSection()
return editProfileForm
2021-07-19 19:40:04 +00:00
def _htmlEditProfileLibreTranslate(translate: {},
libretranslateUrl: str,
libretranslateApiKey: str) -> str:
"""Change automatic translation settings
"""
2021-07-22 16:58:59 +00:00
editProfileForm = beginEditSection('LibreTranslate')
2021-07-19 19:40:04 +00:00
2021-07-22 13:26:43 +00:00
editProfileForm += \
2021-07-22 16:58:59 +00:00
editTextField('URL', 'libretranslateUrl', libretranslateUrl,
'http://0.0.0.0:5000')
2021-07-22 13:26:43 +00:00
editProfileForm += \
2021-07-22 16:58:59 +00:00
editTextField('API Key', 'libretranslateApiKey', libretranslateApiKey)
2021-07-22 13:26:43 +00:00
2021-07-22 16:58:59 +00:00
editProfileForm += endEditSection()
2021-07-19 19:40:04 +00:00
return editProfileForm
2021-12-25 20:20:08 +00:00
def _htmlEditProfileBackground(news_instance: bool, translate: {}) -> str:
"""Background images section of edit profile screen
"""
idx = 'The files attached below should be no larger than ' + \
'10MB in total uploaded at once.'
2021-07-06 10:18:56 +00:00
editProfileForm = \
2021-07-22 16:58:59 +00:00
beginEditSection(translate['Background Images']) + \
' <label class="labels">' + translate[idx] + '</label><br><br>\n'
2021-12-25 20:20:08 +00:00
if not news_instance:
imageFormats = getImageFormats()
editProfileForm += \
' <label class="labels">' + \
2021-07-06 10:18:56 +00:00
translate['Background image'] + '</label>\n' + \
' <input type="file" id="image" name="image"' + \
2021-07-22 10:22:01 +00:00
' accept="' + imageFormats + '">\n' + \
2021-07-06 10:18:56 +00:00
' <br><label class="labels">' + \
translate['Timeline banner image'] + '</label>\n' + \
' <input type="file" id="banner" name="banner"' + \
2021-07-22 10:22:01 +00:00
' accept="' + imageFormats + '">\n' + \
2021-07-06 10:18:56 +00:00
' <br><label class="labels">' + \
translate['Search banner image'] + '</label>\n' + \
' <input type="file" id="search_banner" ' + \
'name="search_banner"' + \
2021-07-22 10:22:01 +00:00
' accept="' + imageFormats + '">\n' + \
2021-07-06 10:18:56 +00:00
' <br><label class="labels">' + \
translate['Left column image'] + '</label>\n' + \
' <input type="file" id="left_col_image" ' + \
'name="left_col_image"' + \
2021-07-22 10:22:01 +00:00
' accept="' + imageFormats + '">\n' + \
2021-07-06 10:18:56 +00:00
' <br><label class="labels">' + \
translate['Right column image'] + '</label>\n' + \
' <input type="file" id="right_col_image" ' + \
'name="right_col_image"' + \
' accept="' + imageFormats + '">\n'
2021-07-22 16:58:59 +00:00
editProfileForm += endEditSection()
return editProfileForm
def _htmlEditProfileContactInfo(nickname: str,
emailAddress: str,
xmppAddress: str,
matrixAddress: str,
ssbAddress: str,
toxAddress: str,
briarAddress: str,
jamiAddress: str,
cwtchAddress: str,
translate: {}) -> str:
"""Contact Information section of edit profile screen
"""
2021-07-22 16:58:59 +00:00
editProfileForm = beginEditSection(translate['Contact Details'])
editProfileForm += editTextField(translate['Email'],
'email', emailAddress)
editProfileForm += editTextField(translate['XMPP'],
'xmppAddress', xmppAddress)
editProfileForm += editTextField(translate['Matrix'],
'matrixAddress', matrixAddress)
editProfileForm += editTextField('SSB', 'ssbAddress', ssbAddress)
editProfileForm += editTextField('Tox', 'toxAddress', toxAddress)
editProfileForm += editTextField('Briar', 'briarAddress', briarAddress)
editProfileForm += editTextField('Jami', 'jamiAddress', jamiAddress)
editProfileForm += editTextField('Cwtch', 'cwtchAddress', cwtchAddress)
editProfileForm += \
'<a href="/users/' + nickname + \
'/followingaccounts"><label class="labels">' + \
translate['Following'] + '</label></a><br>\n'
editProfileForm += endEditSection()
return editProfileForm
def _htmlEditProfileEncryptionKeys(PGPfingerprint: str,
PGPpubKey: str,
EnigmaPubKey: str,
translate: {}) -> str:
"""Contact Information section of edit profile screen
"""
editProfileForm = beginEditSection(translate['Encryption Keys'])
2021-12-11 10:53:38 +00:00
enigmaUrl = 'https://github.com/enigma-reloaded/enigma-reloaded'
editProfileForm += \
editTextField('<a href="' + enigmaUrl + '">Enigma</a>',
'enigmapubkey', EnigmaPubKey)
2021-07-22 16:58:59 +00:00
editProfileForm += editTextField(translate['PGP Fingerprint'],
'openpgp', PGPfingerprint)
editProfileForm += \
2021-07-22 16:58:59 +00:00
editTextArea(translate['PGP'], 'pgp', PGPpubKey, 600,
'-----BEGIN PGP PUBLIC KEY BLOCK-----', False)
2021-07-22 10:22:01 +00:00
2021-07-22 16:58:59 +00:00
editProfileForm += endEditSection()
return editProfileForm
2021-08-12 18:18:50 +00:00
def _htmlEditProfileOptions(isAdmin: bool,
manuallyApprovesFollowers: str,
isBot: str, isGroup: str,
followDMs: str, removeTwitter: str,
notifyLikes: str, notifyReactions: str,
hideLikeButton: str,
2021-11-17 14:25:24 +00:00
hideReactionButton: str,
translate: {}) -> str:
"""option checkboxes section of edit profile screen
"""
editProfileForm = ' <div class="container">\n'
editProfileForm += \
2021-07-22 16:58:59 +00:00
editCheckBox(translate['Approve follower requests'],
'approveFollowers', manuallyApprovesFollowers)
editProfileForm += \
2021-07-22 16:58:59 +00:00
editCheckBox(translate['This is a bot account'],
'isBot', isBot)
2021-08-12 18:18:50 +00:00
if isAdmin:
editProfileForm += \
editCheckBox(translate['This is a group account'],
'isGroup', isGroup)
editProfileForm += \
2021-07-22 16:58:59 +00:00
editCheckBox(translate['Only people I follow can send me DMs'],
'followDMs', followDMs)
editProfileForm += \
2021-07-22 16:58:59 +00:00
editCheckBox(translate['Remove Twitter posts'],
'removeTwitter', removeTwitter)
editProfileForm += \
2021-07-22 16:58:59 +00:00
editCheckBox(translate['Notify when posts are liked'],
'notifyLikes', notifyLikes)
editProfileForm += \
editCheckBox(translate['Notify on emoji reactions'],
'notifyReactions', notifyReactions)
editProfileForm += \
2021-07-22 16:58:59 +00:00
editCheckBox(translate["Don't show the Like button"],
'hideLikeButton', hideLikeButton)
2021-11-17 14:25:24 +00:00
editProfileForm += \
editCheckBox(translate["Don't show the Reaction button"],
'hideReactionButton', hideReactionButton)
editProfileForm += ' </div>\n'
return editProfileForm
2021-12-25 16:17:53 +00:00
def _getSupportedLanguagesSorted(base_dir: str) -> str:
"""Returns a list of supported languages
"""
2021-12-25 16:17:53 +00:00
langList = getSupportedLanguages(base_dir)
2021-07-18 18:57:08 +00:00
if not langList:
return ''
langList.sort()
languagesStr = ''
for lang in langList:
if languagesStr:
languagesStr += ' / ' + lang
else:
languagesStr = lang
return languagesStr
2021-12-25 16:17:53 +00:00
def _htmlEditProfileMain(base_dir: str, displayNickname: str, bioStr: str,
2021-08-12 20:40:23 +00:00
movedTo: str, donateUrl: str, websiteUrl: str,
blogAddress: str, actorJson: {},
translate: {}) -> str:
"""main info on edit profile screen
"""
imageFormats = getImageFormats()
editProfileForm = ' <div class="container">\n'
2021-07-06 10:18:56 +00:00
editProfileForm += \
2021-07-22 16:58:59 +00:00
editTextField(translate['Nickname'], 'displayNickname',
displayNickname)
2021-07-06 10:18:56 +00:00
editProfileForm += \
2021-07-22 18:35:45 +00:00
editTextArea(translate['Your bio'], 'bio', bioStr, 200, '', True)
2021-07-06 10:18:56 +00:00
editProfileForm += \
' <label class="labels">' + translate['Avatar image'] + \
2021-07-06 10:18:56 +00:00
'</label>\n' + \
' <input type="file" id="avatar" name="avatar"' + \
' accept="' + imageFormats + '">\n'
occupationName = ''
if actorJson.get('hasOccupation'):
occupationName = getOccupationName(actorJson)
editProfileForm += \
2021-07-22 16:58:59 +00:00
editTextField(translate['Occupation'], 'occupationName',
occupationName)
alsoKnownAsStr = ''
if actorJson.get('alsoKnownAs'):
alsoKnownAs = actorJson['alsoKnownAs']
ctr = 0
for altActor in alsoKnownAs:
if ctr > 0:
alsoKnownAsStr += ', '
ctr += 1
alsoKnownAsStr += altActor
editProfileForm += \
2021-07-22 16:58:59 +00:00
editTextField(translate['Other accounts'], 'alsoKnownAs',
alsoKnownAsStr, 'https://...')
editProfileForm += \
2021-07-22 16:58:59 +00:00
editTextField(translate['Moved to new account address'], 'movedTo',
movedTo, 'https://...')
2021-07-06 10:18:56 +00:00
editProfileForm += \
2021-07-22 16:58:59 +00:00
editTextField(translate['Donations link'], 'donateUrl',
donateUrl, 'https://...')
2021-07-06 10:18:56 +00:00
2021-08-12 20:40:23 +00:00
editProfileForm += \
2021-08-12 20:56:00 +00:00
editTextField(translate['Website'], 'websiteUrl',
websiteUrl, 'https://...')
2021-08-12 20:40:23 +00:00
editProfileForm += \
2021-07-22 16:58:59 +00:00
editTextField('Blog', 'blogAddress', blogAddress, 'https://...')
2021-12-25 16:17:53 +00:00
languagesListStr = _getSupportedLanguagesSorted(base_dir)
showLanguages = getActorLanguages(actorJson)
editProfileForm += \
2021-07-22 16:58:59 +00:00
editTextField(translate['Languages'], 'showLanguages',
showLanguages, languagesListStr)
2021-07-22 13:26:43 +00:00
2021-07-18 18:48:34 +00:00
editProfileForm += ' </div>\n'
return editProfileForm
2021-12-25 16:17:53 +00:00
def _htmlEditProfileTopBanner(base_dir: str,
2021-12-26 10:00:46 +00:00
nickname: str, domain: str, domain_full: str,
defaultTimeline: str, bannerFile: str,
path: str, accessKeys: {}, translate: {}) -> str:
"""top banner on edit profile screen
"""
editProfileForm = \
2021-07-06 10:18:56 +00:00
'<a href="/users/' + nickname + '/' + defaultTimeline + '">' + \
'<img loading="lazy" class="timeline-banner" src="' + \
'/users/' + nickname + '/' + bannerFile + '" alt="" /></a>\n'
editProfileForm += \
'<form enctype="multipart/form-data" method="POST" ' + \
'accept-charset="UTF-8" action="' + path + '/profiledata">\n'
editProfileForm += ' <div class="vertical-center">\n'
editProfileForm += \
' <h1>' + translate['Profile for'] + \
2021-12-26 10:00:46 +00:00
' ' + nickname + '@' + domain_full + '</h1>'
editProfileForm += ' <div class="container">\n'
editProfileForm += \
' <center>\n' + \
' <input type="submit" name="submitProfile" ' + \
'accesskey="' + accessKeys['submitButton'] + '" ' + \
'value="' + translate['Submit'] + '">\n' + \
' </center>\n'
editProfileForm += ' </div>\n'
2021-12-25 16:17:53 +00:00
if scheduledPostsExist(base_dir, nickname, domain):
editProfileForm += ' <div class="container">\n'
editProfileForm += \
2021-07-22 16:58:59 +00:00
editCheckBox(translate['Remove scheduled posts'],
'removeScheduledPosts', False)
editProfileForm += ' </div>\n'
return editProfileForm
2021-12-25 16:17:53 +00:00
def htmlEditProfile(cssCache: {}, translate: {}, base_dir: str, path: str,
2021-12-25 17:09:22 +00:00
domain: str, port: int, http_prefix: str,
defaultTimeline: str, theme: str,
2021-12-25 23:38:53 +00:00
peertube_instances: [],
2021-12-25 23:09:49 +00:00
text_mode_banner: str, city: str,
2021-12-25 18:27:11 +00:00
user_agents_blocked: str,
accessKeys: {},
2021-12-25 17:31:22 +00:00
default_reply_interval_hrs: int,
2021-12-25 23:26:38 +00:00
cw_lists: {}, lists_enabled: str) -> str:
2020-11-09 22:44:03 +00:00
"""Shows the edit profile screen
"""
path = path.replace('/inbox', '').replace('/outbox', '')
2021-08-09 19:37:18 +00:00
path = path.replace('/shares', '').replace('/wanted', '')
2020-11-09 22:44:03 +00:00
nickname = getNicknameFromActor(path)
if not nickname:
return ''
2021-12-26 10:00:46 +00:00
domain_full = getFullDomain(domain, port)
2020-11-09 22:44:03 +00:00
2021-12-25 16:17:53 +00:00
actorFilename = acctDir(base_dir, nickname, domain) + '.json'
2020-11-09 22:44:03 +00:00
if not os.path.isfile(actorFilename):
return ''
# filename of the banner shown at the top
2020-12-20 18:16:53 +00:00
bannerFile, bannerFilename = \
2021-12-25 16:17:53 +00:00
getBannerFile(base_dir, nickname, domain, theme)
2020-11-09 22:44:03 +00:00
displayNickname = nickname
2021-06-27 16:22:55 +00:00
isBot = isGroup = followDMs = removeTwitter = ''
2021-11-17 14:25:24 +00:00
notifyLikes = notifyReactions = ''
2021-12-25 20:25:07 +00:00
hideLikeButton = hideReactionButton = media_instanceStr = ''
2021-12-25 20:22:25 +00:00
blogs_instanceStr = news_instanceStr = movedTo = twitterStr = ''
2021-12-11 10:53:38 +00:00
bioStr = donateUrl = websiteUrl = emailAddress = ''
PGPpubKey = EnigmaPubKey = ''
2021-06-27 16:22:55 +00:00
PGPfingerprint = xmppAddress = matrixAddress = ''
ssbAddress = blogAddress = toxAddress = jamiAddress = ''
cwtchAddress = briarAddress = manuallyApprovesFollowers = ''
2020-11-09 22:44:03 +00:00
actorJson = loadJson(actorFilename)
if actorJson:
2021-01-12 12:35:30 +00:00
if actorJson.get('movedTo'):
movedTo = actorJson['movedTo']
2020-11-09 22:44:03 +00:00
donateUrl = getDonationUrl(actorJson)
2021-08-12 20:56:00 +00:00
websiteUrl = getWebsite(actorJson, translate)
2020-11-09 22:44:03 +00:00
xmppAddress = getXmppAddress(actorJson)
matrixAddress = getMatrixAddress(actorJson)
ssbAddress = getSSBAddress(actorJson)
blogAddress = getBlogAddress(actorJson)
toxAddress = getToxAddress(actorJson)
2020-12-24 16:48:03 +00:00
briarAddress = getBriarAddress(actorJson)
2020-11-29 12:50:41 +00:00
jamiAddress = getJamiAddress(actorJson)
2021-06-27 11:48:03 +00:00
cwtchAddress = getCwtchAddress(actorJson)
2020-11-09 22:44:03 +00:00
emailAddress = getEmailAddress(actorJson)
2021-12-11 10:53:38 +00:00
EnigmaPubKey = getEnigmaPubKey(actorJson)
2020-11-09 22:44:03 +00:00
PGPpubKey = getPGPpubKey(actorJson)
PGPfingerprint = getPGPfingerprint(actorJson)
if actorJson.get('name'):
2021-12-25 16:17:53 +00:00
if not isFiltered(base_dir, nickname, domain, actorJson['name']):
2020-12-19 12:55:40 +00:00
displayNickname = actorJson['name']
2020-11-09 22:44:03 +00:00
if actorJson.get('summary'):
bioStr = \
actorJson['summary'].replace('<p>', '').replace('</p>', '')
2021-12-25 16:17:53 +00:00
if isFiltered(base_dir, nickname, domain, bioStr):
2020-12-19 12:51:13 +00:00
bioStr = ''
2020-11-09 22:44:03 +00:00
if actorJson.get('manuallyApprovesFollowers'):
if actorJson['manuallyApprovesFollowers']:
manuallyApprovesFollowers = 'checked'
else:
manuallyApprovesFollowers = ''
if actorJson.get('type'):
if actorJson['type'] == 'Service':
isBot = 'checked'
isGroup = ''
elif actorJson['type'] == 'Group':
isGroup = 'checked'
isBot = ''
2021-12-25 16:17:53 +00:00
accountDir = acctDir(base_dir, nickname, domain)
2021-07-13 21:59:53 +00:00
if os.path.isfile(accountDir + '/.followDMs'):
2020-11-09 22:44:03 +00:00
followDMs = 'checked'
2021-07-13 21:59:53 +00:00
if os.path.isfile(accountDir + '/.removeTwitter'):
2020-11-09 22:44:03 +00:00
removeTwitter = 'checked'
2021-07-13 21:59:53 +00:00
if os.path.isfile(accountDir + '/.notifyLikes'):
2020-11-09 22:44:03 +00:00
notifyLikes = 'checked'
if os.path.isfile(accountDir + '/.notifyReactions'):
notifyReactions = 'checked'
2021-07-13 21:59:53 +00:00
if os.path.isfile(accountDir + '/.hideLikeButton'):
2020-11-09 22:44:03 +00:00
hideLikeButton = 'checked'
2021-11-17 14:25:24 +00:00
if os.path.isfile(accountDir + '/.hideReactionButton'):
hideReactionButton = 'checked'
2020-11-09 22:44:03 +00:00
2021-12-25 20:25:07 +00:00
media_instance = getConfigParam(base_dir, "media_instance")
if media_instance:
if media_instance is True:
media_instanceStr = 'checked'
2021-12-25 20:22:25 +00:00
blogs_instanceStr = news_instanceStr = ''
2020-11-09 22:44:03 +00:00
2021-12-25 20:20:08 +00:00
news_instance = getConfigParam(base_dir, "news_instance")
if news_instance:
if news_instance is True:
news_instanceStr = 'checked'
2021-12-25 20:25:07 +00:00
blogs_instanceStr = media_instanceStr = ''
2020-11-09 22:44:03 +00:00
2021-12-25 20:22:25 +00:00
blogs_instance = getConfigParam(base_dir, "blogs_instance")
if blogs_instance:
if blogs_instance is True:
blogs_instanceStr = 'checked'
2021-12-25 20:25:07 +00:00
media_instanceStr = news_instanceStr = ''
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
instanceStr = ''
2021-03-06 13:40:47 +00:00
roleAssignStr = ''
2020-12-24 11:56:17 +00:00
peertubeStr = ''
libretranslateStr = ''
systemMonitorStr = ''
2021-07-20 20:21:25 +00:00
graphicsStr = ''
sharesFederationStr = ''
2020-11-09 22:44:03 +00:00
2021-12-25 16:17:53 +00:00
adminNickname = getConfigParam(base_dir, 'admin')
2021-12-25 16:17:53 +00:00
if isArtist(base_dir, nickname) or \
path.startswith('/users/' + str(adminNickname) + '/'):
2021-12-25 16:17:53 +00:00
graphicsStr = _htmlEditProfileGraphicDesign(base_dir, translate)
2021-08-12 18:18:50 +00:00
isAdmin = False
2020-11-09 22:44:03 +00:00
if adminNickname:
if path.startswith('/users/' + adminNickname + '/'):
2021-08-12 18:18:50 +00:00
isAdmin = True
2021-09-18 17:08:14 +00:00
twitterStr = \
2021-12-25 16:17:53 +00:00
_htmlEditProfileTwitter(base_dir, translate, removeTwitter)
# shared items section
sharesFederationStr = \
2021-12-25 16:17:53 +00:00
_htmlEditProfileSharedItems(base_dir, nickname,
domain, translate)
instanceStr, roleAssignStr, peertubeStr, libretranslateStr = \
2021-12-25 16:17:53 +00:00
_htmlEditProfileInstance(base_dir, translate,
2021-12-25 23:38:53 +00:00
peertube_instances,
2021-12-25 20:25:07 +00:00
media_instanceStr,
2021-12-25 20:22:25 +00:00
blogs_instanceStr,
2021-12-25 20:20:08 +00:00
news_instanceStr)
2021-10-20 10:41:58 +00:00
systemMonitorStr = _htmlSystemMonitor(nickname, translate)
2020-12-24 11:56:17 +00:00
2021-12-25 16:17:53 +00:00
instanceTitle = getConfigParam(base_dir, 'instanceTitle')
editProfileForm = \
htmlHeaderWithExternalStyle(cssFilename, instanceTitle, None)
2020-11-09 22:44:03 +00:00
# keyboard navigation
2021-02-05 17:05:53 +00:00
userPathStr = '/users/' + nickname
userTimalineStr = '/users/' + nickname + '/' + defaultTimeline
2021-02-06 10:35:47 +00:00
menuTimeline = \
2021-02-06 10:46:03 +00:00
htmlHideFromScreenReader('🏠') + ' ' + \
translate['Switch to timeline view']
2021-02-06 10:35:47 +00:00
menuProfile = \
2021-02-06 10:46:03 +00:00
htmlHideFromScreenReader('👤') + ' ' + \
translate['Switch to profile view']
2021-02-05 17:05:53 +00:00
navLinks = {
2021-02-06 10:35:47 +00:00
menuProfile: userPathStr,
menuTimeline: userTimalineStr
2021-02-05 17:05:53 +00:00
}
2021-04-22 11:51:19 +00:00
navAccessKeys = {
menuProfile: 'p',
menuTimeline: 't'
}
2021-12-25 23:09:49 +00:00
editProfileForm += htmlKeyboardNavigation(text_mode_banner,
2021-04-22 11:51:19 +00:00
navLinks, navAccessKeys)
# top banner
2021-02-05 15:46:05 +00:00
editProfileForm += \
2021-12-26 10:00:46 +00:00
_htmlEditProfileTopBanner(base_dir, nickname, domain, domain_full,
defaultTimeline, bannerFile,
path, accessKeys, translate)
2020-11-09 22:44:03 +00:00
# main info
2021-01-22 22:12:31 +00:00
editProfileForm += \
2021-12-25 16:17:53 +00:00
_htmlEditProfileMain(base_dir, displayNickname, bioStr,
2021-08-12 20:40:23 +00:00
movedTo, donateUrl, websiteUrl,
blogAddress, actorJson, translate)
2021-03-06 11:57:40 +00:00
# Option checkboxes
editProfileForm += \
2021-08-12 18:18:50 +00:00
_htmlEditProfileOptions(isAdmin, manuallyApprovesFollowers,
isBot, isGroup, followDMs, removeTwitter,
notifyLikes, notifyReactions,
2021-11-17 14:25:24 +00:00
hideLikeButton, hideReactionButton,
translate)
2021-03-06 11:57:40 +00:00
# Contact information
2020-11-09 22:44:03 +00:00
editProfileForm += \
_htmlEditProfileContactInfo(nickname, emailAddress,
xmppAddress, matrixAddress,
ssbAddress, toxAddress,
briarAddress, jamiAddress,
cwtchAddress, translate)
# Encryption Keys
editProfileForm += \
_htmlEditProfileEncryptionKeys(PGPfingerprint,
PGPpubKey, EnigmaPubKey, translate)
2021-03-06 11:55:09 +00:00
# Customize images and banners
2021-12-25 20:20:08 +00:00
editProfileForm += _htmlEditProfileBackground(news_instance, translate)
2021-03-06 11:55:09 +00:00
# Change password
editProfileForm += _htmlEditProfileChangePassword(translate)
2020-11-09 22:44:03 +00:00
2021-07-19 19:40:04 +00:00
# automatic translations
editProfileForm += libretranslateStr
2021-07-19 19:40:04 +00:00
# system monitor
editProfileForm += systemMonitorStr
# Filtering and blocking section
2021-12-25 16:17:53 +00:00
replyIntervalHours = getReplyIntervalHours(base_dir, nickname, domain,
2021-12-25 17:31:22 +00:00
default_reply_interval_hrs)
2020-11-09 22:44:03 +00:00
editProfileForm += \
2021-12-25 16:17:53 +00:00
_htmlEditProfileFiltering(base_dir, nickname, domain,
2021-12-25 18:27:11 +00:00
user_agents_blocked, translate,
2021-10-21 16:58:01 +00:00
replyIntervalHours,
2021-12-25 23:26:38 +00:00
cw_lists, lists_enabled)
# git projects section
2020-11-09 22:44:03 +00:00
editProfileForm += \
2021-12-25 16:17:53 +00:00
_htmlEditProfileGitProjects(base_dir, nickname, domain, translate)
# Skills section
2020-11-09 22:44:03 +00:00
editProfileForm += \
2021-12-25 16:17:53 +00:00
_htmlEditProfileSkills(base_dir, nickname, domain, translate)
editProfileForm += roleAssignStr + peertubeStr + graphicsStr
2021-09-18 17:08:14 +00:00
editProfileForm += sharesFederationStr + twitterStr + instanceStr
2021-03-06 15:18:56 +00:00
# danger zone section
editProfileForm += _htmlEditProfileDangerZone(translate)
editProfileForm += ' <div class="container">\n'
editProfileForm += \
' <center>\n' + \
' <input type="submit" name="submitProfile" value="' + \
translate['Submit'] + '">\n' + \
' </center>\n'
editProfileForm += ' </div>\n'
2020-11-09 22:44:03 +00:00
editProfileForm += ' </div>\n'
editProfileForm += '</form>\n'
editProfileForm += htmlFooter()
return editProfileForm
2021-12-25 23:03:28 +00:00
def _individualFollowAsHtml(signing_priv_key_pem: str,
translate: {},
2021-12-25 16:17:53 +00:00
base_dir: str, session,
2021-12-25 22:28:18 +00:00
cached_webfingers: {},
2021-12-25 22:17:49 +00:00
person_cache: {}, domain: str,
followUrl: str,
authorized: bool,
actorNickname: str,
2021-12-25 17:09:22 +00:00
http_prefix: str,
2021-12-25 20:34:38 +00:00
project_version: str,
dormant: bool,
2021-03-14 19:22:58 +00:00
debug: bool,
buttons=[]) -> str:
2020-11-09 22:44:03 +00:00
"""An individual follow entry on the profile screen
"""
followUrlNickname = getNicknameFromActor(followUrl)
followUrlDomain, followUrlPort = getDomainFromActor(followUrl)
followUrlDomainFull = getFullDomain(followUrlDomain, followUrlPort)
titleStr = '@' + followUrlNickname + '@' + followUrlDomainFull
2021-12-25 22:17:49 +00:00
avatarUrl = getPersonAvatarUrl(base_dir, followUrl, person_cache, True)
2020-11-09 22:44:03 +00:00
if not avatarUrl:
avatarUrl = followUrl + '/avatar.png'
2021-12-25 22:17:49 +00:00
displayName = getDisplayName(base_dir, followUrl, person_cache)
isGroup = False
if not displayName:
# lookup the correct webfinger for the followUrl
followUrlHandle = followUrlNickname + '@' + followUrlDomainFull
followUrlWf = \
2021-12-25 17:09:22 +00:00
webfingerHandle(session, followUrlHandle, http_prefix,
2021-12-25 22:28:18 +00:00
cached_webfingers,
domain, __version__, debug, False,
2021-12-25 23:03:28 +00:00
signing_priv_key_pem)
originDomain = domain
(inboxUrl, pubKeyId, pubKey, fromPersonId, sharedInbox, avatarUrl2,
2021-12-25 23:03:28 +00:00
displayName, isGroup) = getPersonBox(signing_priv_key_pem,
originDomain,
2021-12-25 16:17:53 +00:00
base_dir, session,
followUrlWf,
2021-12-25 22:17:49 +00:00
person_cache, project_version,
2021-12-25 17:09:22 +00:00
http_prefix, followUrlNickname,
domain, 'outbox', 43036)
if avatarUrl2:
avatarUrl = avatarUrl2
if displayName:
2021-02-12 17:08:48 +00:00
displayName = \
2021-12-25 17:09:22 +00:00
addEmojiToDisplayName(None, base_dir, http_prefix,
2021-02-12 17:08:48 +00:00
actorNickname, domain,
displayName, False)
2021-01-12 10:23:25 +00:00
titleStr = displayName
if dormant:
titleStr += ' 💤'
2020-11-09 22:44:03 +00:00
buttonsStr = ''
if authorized:
for b in buttons:
if b == 'block':
buttonsStr += \
'<a href="/users/' + actorNickname + \
'?options=' + followUrl + \
';1;' + avatarUrl + '"><button class="buttonunfollow">' + \
translate['Block'] + '</button></a>\n'
2021-07-06 10:18:56 +00:00
elif b == 'unfollow':
unfollowStr = 'Unfollow'
if isGroup or \
2021-12-25 16:17:53 +00:00
isGroupAccount(base_dir,
followUrlNickname, followUrlDomain):
unfollowStr = 'Leave'
2020-11-09 22:44:03 +00:00
buttonsStr += \
'<a href="/users/' + actorNickname + \
'?options=' + followUrl + \
';1;' + avatarUrl + '"><button class="buttonunfollow">' + \
translate[unfollowStr] + '</button></a>\n'
2020-11-09 22:44:03 +00:00
resultStr = '<div class="container">\n'
resultStr += \
'<a href="/users/' + actorNickname + '?options=' + \
followUrl + ';1;' + avatarUrl + '">\n'
resultStr += '<p><img loading="lazy" src="' + avatarUrl + '" alt=" ">'
resultStr += titleStr + '</a>' + buttonsStr + '</p>\n'
resultStr += '</div>\n'
return resultStr