__filename__ = "webapp_profile.py"
__author__ = "Bob Mottram"
__license__ = "AGPL3+"
__version__ = "1.2.0"
__maintainer__ = "Bob Mottram"
__email__ = "bob@libreserver.org"
__status__ = "Production"
__module_group__ = "Web Interface"
import os
from pprint import pprint
from webfinger import webfingerHandle
from utils import getDisplayName
from utils import isGroupAccount
from utils import hasObjectDict
from utils import getOccupationName
from utils import getLockedAccount
from utils import getFullDomain
from utils import isArtist
from utils import isDormant
from utils import getNicknameFromActor
from utils import getDomainFromActor
from utils import isSystemAccount
from utils import removeHtml
from utils import loadJson
from utils import getConfigParam
from utils import getImageFormats
from utils import acctDir
from utils import getSupportedLanguages
from utils import localActorUrl
from utils import getReplyIntervalHours
from languages import getActorLanguages
from skills import getSkills
from theme import getThemesList
from person import personBoxJson
from person import getActorJson
from person import getPersonAvatarUrl
from posts import getPersonBox
from posts import isModerator
from posts import parseUserFeed
from posts import isCreateInsideAnnounce
from donate import getDonationUrl
from donate import getWebsite
from xmpp import getXmppAddress
from matrix import getMatrixAddress
from ssb import getSSBAddress
from pgp import getEmailAddress
from pgp import getPGPfingerprint
from pgp import getPGPpubKey
from tox import getToxAddress
from briar import getBriarAddress
from jami import getJamiAddress
from cwtch import getCwtchAddress
from filters import isFiltered
from follow import isFollowerOfPerson
from follow import getFollowerDomains
from webapp_frontscreen import htmlFrontScreen
from webapp_utils import htmlKeyboardNavigation
from webapp_utils import htmlHideFromScreenReader
from webapp_utils import scheduledPostsExist
from webapp_utils import htmlHeaderWithExternalStyle
from webapp_utils import htmlHeaderWithPersonMarkup
from webapp_utils import htmlFooter
from webapp_utils import addEmojiToDisplayName
from webapp_utils import getBannerFile
from webapp_utils import htmlPostSeparator
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
from blog import getBlogAddress
from webapp_post import individualPostAsHtml
from webapp_timeline import htmlIndividualShare
from blocking import getCWlistVariable
def htmlProfileAfterSearch(cssCache: {},
recentPostsCache: {}, maxRecentPosts: int,
translate: {},
baseDir: str, path: str, httpPrefix: str,
nickname: str, domain: str, port: int,
profileHandle: str,
session, cachedWebfingers: {}, personCache: {},
debug: bool, projectVersion: str,
YTReplacementDomain: str,
twitterReplacementDomain: str,
showPublishedDateOnly: bool,
defaultTimeline: str,
peertubeInstances: [],
allowLocalNetworkAccess: bool,
themeName: str,
accessKeys: {},
systemLanguage: str,
maxLikeCount: int,
signingPrivateKeyPem: str,
CWlists: {}, listsEnabled: str) -> str:
"""Show a profile page after a search for a fediverse address
"""
http = False
gnunet = False
if httpPrefix == 'http':
http = True
elif httpPrefix == 'gnunet':
gnunet = True
profileJson, asHeader = \
getActorJson(domain, profileHandle, http, gnunet, debug, False,
signingPrivateKeyPem)
if not profileJson:
return None
personUrl = profileJson['id']
searchDomain, searchPort = getDomainFromActor(personUrl)
if not searchDomain:
return None
searchNickname = getNicknameFromActor(personUrl)
if not searchNickname:
return None
searchDomainFull = getFullDomain(searchDomain, searchPort)
profileStr = ''
cssFilename = baseDir + '/epicyon-profile.css'
if os.path.isfile(baseDir + '/epicyon.css'):
cssFilename = baseDir + '/epicyon.css'
isGroup = False
if profileJson.get('type'):
if profileJson['type'] == 'Group':
isGroup = True
avatarUrl = ''
if profileJson.get('icon'):
if profileJson['icon'].get('url'):
avatarUrl = profileJson['icon']['url']
if not avatarUrl:
avatarUrl = getPersonAvatarUrl(baseDir, personUrl,
personCache, True)
displayName = searchNickname
if profileJson.get('name'):
displayName = profileJson['name']
lockedAccount = getLockedAccount(profileJson)
if lockedAccount:
displayName += '🔒'
movedTo = ''
if profileJson.get('movedTo'):
movedTo = profileJson['movedTo']
if '"' in movedTo:
movedTo = movedTo.split('"')[1]
displayName += ' ⌂'
followsYou = \
isFollowerOfPerson(baseDir,
nickname, domain,
searchNickname,
searchDomainFull)
profileDescription = ''
if profileJson.get('summary'):
profileDescription = profileJson['summary']
outboxUrl = None
if not profileJson.get('outbox'):
if debug:
pprint(profileJson)
print('DEBUG: No outbox found')
return None
outboxUrl = profileJson['outbox']
# profileBackgroundImage = ''
# if profileJson.get('image'):
# if profileJson['image'].get('url'):
# profileBackgroundImage = profileJson['image']['url']
# 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:
profileDescriptionShort = ''
else:
if '
' in profileDescription:
if len(profileDescription.split('
')) > 2:
profileDescriptionShort = ''
# keep the profile description short
if len(profileDescriptionShort) > 256:
profileDescriptionShort = ''
# remove formatting from profile description used on title
avatarDescription = ''
if profileJson.get('summary'):
if isinstance(profileJson['summary'], str):
avatarDescription = \
profileJson['summary'].replace('
', '\n')
avatarDescription = avatarDescription.replace('
', '') avatarDescription = avatarDescription.replace('
', '') if '<' in avatarDescription: avatarDescription = removeHtml(avatarDescription) imageUrl = '' if profileJson.get('image'): if profileJson['image'].get('url'): imageUrl = profileJson['image']['url'] alsoKnownAs = None if profileJson.get('alsoKnownAs'): alsoKnownAs = profileJson['alsoKnownAs'] joinedDate = None if profileJson.get('published'): if 'T' in profileJson['published']: joinedDate = profileJson['published'] profileStr = \ _getProfileHeaderAfterSearch(baseDir, nickname, defaultTimeline, searchNickname, searchDomainFull, translate, displayName, followsYou, profileDescriptionShort, avatarUrl, imageUrl, movedTo, profileJson['id'], alsoKnownAs, accessKeys, joinedDate) domainFull = getFullDomain(domain, port) followIsPermitted = True if not profileJson.get('followers'): # no followers collection specified within actor followIsPermitted = False elif searchNickname == 'news' and searchDomainFull == domainFull: # currently the news actor is not something you can follow followIsPermitted = False elif searchNickname == nickname and searchDomainFull == domainFull: # don't follow yourself! followIsPermitted = False if followIsPermitted: followStr = 'Follow' if isGroup: followStr = 'Join' profileStr += \ '' + translate['Website'] + ': ' + websiteUrl + '
\n' if emailAddress: donateSection += \ '' + translate['Email'] + ': ' + emailAddress + '
\n' if blogAddress: donateSection += \ 'Blog: ' + blogAddress + '
\n' if xmppAddress: donateSection += \ '' + translate['XMPP'] + ': ' + xmppAddress + '
\n' if matrixAddress: donateSection += \ '' + translate['Matrix'] + ': ' + matrixAddress + '
\n' if ssbAddress: donateSection += \ 'SSB:
\n' if toxAddress: donateSection += \ 'Tox:
\n' if briarAddress: if briarAddress.startswith('briar://'): donateSection += \ '\n' else: donateSection += \ 'briar://
\n' if jamiAddress: donateSection += \ 'Jami:
\n' if cwtchAddress: donateSection += \ 'Cwtch:
\n' if PGPfingerprint: donateSection += \ 'PGP: ' + \
PGPfingerprint.replace('\n', '
') + '
' + PGPpubKey.replace('\n', '
') + '
', '') avatarDescription = avatarDescription.replace('
', '') movedTo = '' if profileJson.get('movedTo'): movedTo = profileJson['movedTo'] if '"' in movedTo: movedTo = movedTo.split('"')[1] alsoKnownAs = None if profileJson.get('alsoKnownAs'): alsoKnownAs = profileJson['alsoKnownAs'] joinedDate = None if profileJson.get('published'): if 'T' in profileJson['published']: joinedDate = profileJson['published'] occupationName = None if profileJson.get('hasOccupation'): occupationName = getOccupationName(profileJson) avatarUrl = profileJson['icon']['url'] # use alternate path for local avatars to avoid any caching issues if '://' + domainFull + '/system/accounts/avatars/' in avatarUrl: avatarUrl = \ avatarUrl.replace('://' + domainFull + '/system/accounts/avatars/', '://' + domainFull + '/users/') # get pinned post content accountDir = acctDir(baseDir, nickname, domain) pinnedFilename = accountDir + '/pinToProfile.txt' pinnedContent = None if os.path.isfile(pinnedFilename): with open(pinnedFilename, 'r') as pinFile: pinnedContent = pinFile.read() profileHeaderStr = \ _getProfileHeader(baseDir, httpPrefix, nickname, domain, domainFull, translate, defaultTimeline, displayName, avatarDescription, profileDescriptionShort, loginButton, avatarUrl, theme, movedTo, alsoKnownAs, pinnedContent, accessKeys, joinedDate, occupationName) # keyboard navigation userPathStr = '/users/' + nickname deft = defaultTimeline isGroup = False followersStr = translate['Followers'] if isGroupAccount(baseDir, nickname, domain): isGroup = True followersStr = translate['Members'] menuTimeline = \ htmlHideFromScreenReader('🏠') + ' ' + \ translate['Switch to timeline view'] menuEdit = \ htmlHideFromScreenReader('✍') + ' ' + translate['Edit'] if not isGroup: menuFollowing = \ htmlHideFromScreenReader('👥') + ' ' + translate['Following'] menuFollowers = \ htmlHideFromScreenReader('👪') + ' ' + followersStr if not isGroup: menuRoles = \ htmlHideFromScreenReader('🤚') + ' ' + translate['Roles'] menuSkills = \ htmlHideFromScreenReader('🛠') + ' ' + translate['Skills'] menuLogout = \ htmlHideFromScreenReader('❎') + ' ' + translate['Logout'] navLinks = { menuTimeline: userPathStr + '/' + deft, menuEdit: userPathStr + '/editprofile', menuFollowing: userPathStr + '/following#timeline', menuFollowers: userPathStr + '/followers#timeline', menuRoles: userPathStr + '/roles#timeline', menuSkills: userPathStr + '/skills#timeline', menuLogout: '/logout' } navAccessKeys = {} for variableName, key in accessKeys.items(): if not locals().get(variableName): continue navAccessKeys[locals()[variableName]] = key profileStr = htmlKeyboardNavigation(textModeBanner, navLinks, navAccessKeys) profileStr += profileHeaderStr + donateSection profileStr += '@' + nickname + '@' + domain + ' has no roles assigned
\n' else: profileStr = '