diff --git a/announce.py b/announce.py index 814728c15..92c412c5c 100644 --- a/announce.py +++ b/announce.py @@ -77,7 +77,7 @@ def outboxAnnounce(recentPostsCache: {}, nickname, domain, debug) return True elif messageJson['type'] == 'Undo': - if not isinstance(messageJson['object'], dict): + if not hasObjectDict(messageJson): return False if not messageJson['object'].get('type'): return False diff --git a/inbox.py b/inbox.py index d1f6d2450..8fbaccf50 100644 --- a/inbox.py +++ b/inbox.py @@ -298,7 +298,7 @@ def inboxMessageHasParams(messageJson: {}) -> bool: return False # object should be a dict or a string - if not isinstance(messageJson['object'], dict): + if not hasObjectDict(messageJson): if not isinstance(messageJson['object'], str): print('WARN: object from ' + str(messageJson['actor']) + ' should be a dict or string, but is actually: ' + diff --git a/media.py b/media.py index 2f788117e..0206a10ba 100644 --- a/media.py +++ b/media.py @@ -18,6 +18,7 @@ from utils import getImageExtensions from utils import getVideoExtensions from utils import getAudioExtensions from utils import getMediaExtensions +from utils import hasObjectDict from shutil import copyfile from shutil import rmtree from shutil import move @@ -30,7 +31,7 @@ def replaceYouTube(postJsonObject: {}, replacementDomain: str) -> None: """ if not replacementDomain: return - if not isinstance(postJsonObject['object'], dict): + if not hasObjectDict(postJsonObject): return if not postJsonObject['object'].get('content'): return diff --git a/posts.py b/posts.py index a213f7444..0dc9e7179 100644 --- a/posts.py +++ b/posts.py @@ -1296,7 +1296,7 @@ def _postIsAddressedToFollowers(baseDir: str, toList = [] ccList = [] if postJsonObject['type'] != 'Update' and \ - isinstance(postJsonObject['object'], dict): + hasObjectDict(postJsonObject): if postJsonObject['object'].get('to'): toList = postJsonObject['object']['to'] if postJsonObject['object'].get('cc'): @@ -2169,7 +2169,7 @@ def _addFollowersToPublicPost(postJsonObject: {}) -> None: if postJsonObject.get('cc'): return postJsonObject['cc'] = postJsonObject['actor'] + '/followers' - elif isinstance(postJsonObject['object'], dict): + elif hasObjectDict(postJsonObject): if not postJsonObject['object'].get('to'): return if len(postJsonObject['object']['to']) > 1: @@ -2433,7 +2433,7 @@ def sendToNamedAddresses(session, baseDir: str, if not postJsonObject.get('object'): return isProfileUpdate = False - if isinstance(postJsonObject['object'], dict): + if hasObjectDict(postJsonObject): if _isProfileUpdate(postJsonObject): # use the original object, which has a 'to' recipientsObject = postJsonObject diff --git a/question.py b/question.py index 9008cbd56..60deecdc4 100644 --- a/question.py +++ b/question.py @@ -126,7 +126,7 @@ def isQuestion(postObjectJson: {}) -> bool: if postObjectJson['type'] != 'Create' and \ postObjectJson['type'] != 'Update': return False - if not isinstance(postObjectJson['object'], dict): + if not hasObjectDict(postObjectJson): return False if not postObjectJson['object'].get('type'): return False diff --git a/theme.py b/theme.py index a700d9706..46062ca8a 100644 --- a/theme.py +++ b/theme.py @@ -366,16 +366,12 @@ def _setThemeFromDict(baseDir: str, name: str, with open(filename, 'w+') as cssfile: cssfile.write(css) - if bgParams.get('login'): - _setBackgroundFormat(baseDir, name, 'login', bgParams['login']) - if bgParams.get('follow'): - _setBackgroundFormat(baseDir, name, 'follow', bgParams['follow']) - if bgParams.get('options'): - _setBackgroundFormat(baseDir, name, 'options', bgParams['options']) - if bgParams.get('search'): - _setBackgroundFormat(baseDir, name, 'search', bgParams['search']) - if bgParams.get('welcome'): - _setBackgroundFormat(baseDir, name, 'welcome', bgParams['welcome']) + screenName = ( + 'login', 'follow', 'options', 'search', 'welcome' + ) + for s in screenName: + if bgParams.get(s): + _setBackgroundFormat(baseDir, name, s, bgParams[s]) def _setBackgroundFormat(baseDir: str, name: str, diff --git a/utils.py b/utils.py index 6e291f1ae..9d21085f2 100644 --- a/utils.py +++ b/utils.py @@ -1401,7 +1401,7 @@ def deletePost(baseDir: str, httpPrefix: str, # remove from moderation index file if hasObject: - if isinstance(postJsonObject['object'], dict): + if hasObjectDict(postJsonObject): if postJsonObject['object'].get('moderationStatus'): if postJsonObject.get('id'): postId = removeIdEnding(postJsonObject['id']) @@ -2536,3 +2536,15 @@ def getPortFromDomain(domain: str) -> int: if portStr.isdigit(): return int(portStr) return None + + +def validUrlPrefix(url: str) -> bool: + """Does the given url have a valid prefix? + """ + if '/' not in url: + return False + prefixes = ('https:', 'http:', 'hyper:', 'i2p:', 'gnunet:') + for pre in prefixes: + if url.startswith(pre): + return True + return False diff --git a/webapp_calendar.py b/webapp_calendar.py index 65cdb7de0..a01ffeb55 100644 --- a/webapp_calendar.py +++ b/webapp_calendar.py @@ -117,8 +117,7 @@ def _htmlCalendarDay(personCache: {}, cssCache: {}, translate: {}, if '/users/' in actor: calActor = '/users/' + actor.split('/users/')[1] - instanceTitle = \ - getConfigParam(baseDir, 'instanceTitle') + instanceTitle = getConfigParam(baseDir, 'instanceTitle') calendarStr = htmlHeaderWithExternalStyle(cssFilename, instanceTitle) calendarStr += '
\n' calendarStr += '\n' calendarStr += '\n' calendarStr += '\n' - calendarStr += ' \n' - calendarStr += ' \n' - calendarStr += ' \n' - calendarStr += ' \n' - calendarStr += ' \n' - calendarStr += ' \n' - calendarStr += ' \n' + days = ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat') + for d in days: + calendarStr += ' \n' calendarStr += '\n' calendarStr += '\n' calendarStr += '\n' diff --git a/webapp_column_left.py b/webapp_column_left.py index eea4ace02..09b73f782 100644 --- a/webapp_column_left.py +++ b/webapp_column_left.py @@ -51,10 +51,8 @@ def _getLeftColumnShares(baseDir: str, if '<' in sharedesc or '?' in sharedesc: continue contactActor = item['actor'] - shareLink = actor + \ - '?replydm=sharedesc:' + \ - sharedesc.replace(' ', '_') + \ - '?mention=' + contactActor + shareLink = actor + '?replydm=sharedesc:' + \ + sharedesc.replace(' ', '_') + '?mention=' + contactActor linksList.append(sharedesc + ' ' + shareLink) ctr += 1 if ctr >= maxSharesInLeftColumn: @@ -89,16 +87,14 @@ def getLeftColumnContent(baseDir: str, nickname: str, domainFull: str, if os.path.isfile(leftColumnImageFilename): editImageClass = 'leftColEditImage' htmlStr += \ - '\n
\n' + \ - ' \n \n' + \ '
\n' if showBackButton: htmlStr += \ - '
' + \ - ' ' + \ + '
' + \ '\n' @@ -112,14 +108,11 @@ def getLeftColumnContent(baseDir: str, nickname: str, domainFull: str, if editor: # show the edit icon htmlStr += \ - ' ' + \ - '' + \
+            '<img class=\n' + translate['Edit Links'] + '" src="/icons/edit.png" />\n' # RSS icon if nickname != 'news': @@ -134,10 +127,8 @@ def getLeftColumnContent(baseDir: str, nickname: str, domainFull: str, else: rssTitle = translate['RSS feed for this site'] rssIconStr = \ - ' ' + \ - '' + rssTitle + \
-        '' + rssTitle + '\n' if rssIconAtTop: htmlStr += rssIconStr @@ -326,8 +317,7 @@ def htmlLinksMobile(cssCache: {}, baseDir: str, theme, accessKeys) else: if editor: - htmlStr += '


\n' - htmlStr += '
\n ' + htmlStr += '


\n
\n ' htmlStr += translate['Select the edit icon to add web links'] htmlStr += '\n
\n' @@ -376,7 +366,8 @@ def htmlEditLinks(cssCache: {}, translate: {}, baseDir: str, path: str, translate['Switch to timeline view'] + '" alt="' + \ translate['Switch to timeline view'] + '" ' + \ 'accesskey="' + accessKeys['menuTimeline'] + '">\n' - editLinksForm += '\n' + \ '\n' @@ -411,8 +402,7 @@ def htmlEditLinks(cssCache: {}, translate: {}, baseDir: str, path: str, '
' editLinksForm += \ ' ' + 'style="height:80vh" spellcheck="false">' + linksStr + '' editLinksForm += \ '
' diff --git a/webapp_column_right.py b/webapp_column_right.py index 3ea61e1c9..731ac86bf 100644 --- a/webapp_column_right.py +++ b/webapp_column_right.py @@ -253,8 +253,7 @@ def _htmlNewswire(baseDir: str, newswire: {}, nickname: str, moderator: bool, if faviconUrl: faviconLink = \ '' + 'alt="" ' + _getBrokenFavSubstitute() + '/>' moderatedItem = item[5] htmlStr += separatorStr if moderatedItem and 'vote:' + nickname in item[2]: @@ -270,8 +269,7 @@ def _htmlNewswire(baseDir: str, newswire: {}, nickname: str, moderator: bool, '' + \ '' + \ - faviconLink + title + \ - '' + totalVotesStr + faviconLink + title + '' + totalVotesStr if moderator: htmlStr += \ ' ' + dateShown + '' + \ '' + \ - faviconLink + title + '' + \ - totalVotesStr + faviconLink + title + '' + totalVotesStr htmlStr += ' ' htmlStr += dateShown + '

\n' diff --git a/webapp_confirm.py b/webapp_confirm.py index 69f0cc576..fdbdc0ff5 100644 --- a/webapp_confirm.py +++ b/webapp_confirm.py @@ -137,8 +137,7 @@ def htmlConfirmRemoveSharedItem(cssCache: {}, translate: {}, baseDir: str, if os.path.isfile(baseDir + '/follow.css'): cssFilename = baseDir + '/follow.css' - instanceTitle = \ - getConfigParam(baseDir, 'instanceTitle') + instanceTitle = getConfigParam(baseDir, 'instanceTitle') sharesStr = htmlHeaderWithExternalStyle(cssFilename, instanceTitle) sharesStr += '
\n' @@ -369,20 +368,10 @@ def htmlCalendar(personCache: {}, cssCache: {}, translate: {}, calendarStr += '
' + \ - translate['Sun'] + '' + \ - translate['Mon'] + '' + \ - translate['Tue'] + '' + \ - translate['Wed'] + '' + \ - translate['Thu'] + '' + \ - translate['Fri'] + '' + \ - translate['Sat'] + '' + \ + translate[d] + '
\n' + newPostForm += ' \n\n' newPostForm += ' \n' newPostForm += '
\n' - # newPostForm += \ - # ' \n' - newPostForm += '
\n' newPostForm += replyStr @@ -777,14 +761,13 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {}, if not mediaInstance or replyStr: newPostForm += newPostImageSection - newPostForm += '
\n' newPostForm += \ + '
\n' + \ ' \n' - newPostForm += '
\n' - - newPostForm += '
\n' - newPostForm += '\n' + submitText + '">\n' + \ + ' \n' + \ + ' \n' + \ + '\n' if not reportUrl: newPostForm = \ diff --git a/webapp_frontscreen.py b/webapp_frontscreen.py index 585489077..a1c575029 100644 --- a/webapp_frontscreen.py +++ b/webapp_frontscreen.py @@ -128,22 +128,24 @@ def htmlFrontScreen(rssIconAtTop: bool, if loginButton: profileHeaderStr += '
' + loginButton + '
\n' - profileHeaderStr += '\n' - profileHeaderStr += ' \n' - profileHeaderStr += ' \n' - profileHeaderStr += ' \n' - profileHeaderStr += ' \n' - profileHeaderStr += ' \n' - profileHeaderStr += ' \n' - profileHeaderStr += ' \n' - profileHeaderStr += ' \n' + \ + ' \n' + \ + ' \n' + \ + '
\n' + profileHeaderStr += \ + '\n' + \ + ' \n' + \ + ' \n' + \ + ' \n' + \ + ' \n' + \ + ' \n' + \ + ' \n' + \ + ' \n' + \ + ' \n' - profileHeaderStr += ' \n' + \ + ' \n' - profileFooterStr += ' \n' - profileFooterStr += ' \n' - profileFooterStr += '
\n' profileHeaderStr += \ getLeftColumnContent(baseDir, 'news', domainFull, httpPrefix, translate, False, False, None, rssIconAtTop, True, True, theme, accessKeys) - profileHeaderStr += ' \n' + profileHeaderStr += \ + ' \n' profileStr = profileHeaderStr @@ -177,10 +179,11 @@ def htmlFrontScreen(rssIconAtTop: bool, False, None, False, False, False, True, authorized, True, theme, defaultTimeline, accessKeys) - profileFooterStr += '
\n' + profileFooterStr += \ + '
\n' instanceTitle = \ getConfigParam(baseDir, 'instanceTitle') diff --git a/webapp_hashtagswarm.py b/webapp_hashtagswarm.py index cab2af8b8..8cb784d8b 100644 --- a/webapp_hashtagswarm.py +++ b/webapp_hashtagswarm.py @@ -29,17 +29,19 @@ def getHashtagCategoriesFeed(baseDir: str, if not hashtagCategories: return None - rssStr = "\n" - rssStr += "\n" - rssStr += '\n' - rssStr += ' #categories\n' + rssStr = \ + "\n" + \ + "\n" + \ + '\n' + \ + ' #categories\n' rssDateStr = \ datetime.utcnow().strftime("%a, %d %b %Y %H:%M:%S UT") for categoryStr, hashtagList in hashtagCategories.items(): - rssStr += '\n' - rssStr += ' ' + categoryStr + '\n' + rssStr += \ + '\n' + \ + ' ' + categoryStr + '\n' listStr = '' for hashtag in hashtagList: if ':' in hashtag: @@ -47,75 +49,18 @@ def getHashtagCategoriesFeed(baseDir: str, if '&' in hashtag: continue listStr += hashtag + ' ' - rssStr += ' ' + listStr.strip() + '\n' - rssStr += ' \n' - rssStr += ' ' + rssDateStr + '\n' - rssStr += '\n' + rssStr += \ + ' ' + listStr.strip() + '\n' + \ + ' \n' + \ + ' ' + rssDateStr + '\n' + \ + '\n' - rssStr += '\n' - rssStr += '\n' + rssStr += \ + '\n' + \ + '\n' return rssStr -def _getHashtagDomainMax(domainHistogram: {}) -> str: - """Returns the domain with the maximum number of hashtags - """ - maxCount = 1 - maxDomain = None - for domain, count in domainHistogram.items(): - if count > maxCount: - maxDomain = domain - maxCount = count - return maxDomain - - -def _getHashtagDomainHistogram(domainHistogram: {}, translate: {}) -> str: - """Returns the html for a histogram of domains - from which hashtags are coming - """ - totalCount = 0 - for domain, count in domainHistogram.items(): - totalCount += count - if totalCount == 0: - return '' - - htmlStr = '' - histogramHeaderStr = '

\n' - histogramHeaderStr += '

' + translate['Hashtag origins'] + '

\n' - histogramHeaderStr += ' \n' - histogramHeaderStr += ' \n' - histogramHeaderStr += ' \n' - histogramHeaderStr += ' \n' - histogramHeaderStr += ' \n' - histogramHeaderStr += ' \n' - histogramHeaderStr += ' \n' - - leftColStr = '' - rightColStr = '' - - for i in range(len(domainHistogram)): - domain = _getHashtagDomainMax(domainHistogram) - if not domain: - break - percent = int(domainHistogram[domain] * 100 / totalCount) - if histogramHeaderStr: - htmlStr += histogramHeaderStr - histogramHeaderStr = None - leftColStr += str(percent) + '%
' - rightColStr += domain + '
' - del domainHistogram[domain] - - if htmlStr: - htmlStr += ' \n' - htmlStr += ' \n' - htmlStr += ' \n' - htmlStr += ' \n' - htmlStr += '
' + leftColStr + '' + rightColStr + '
\n' - htmlStr += '
\n' - - return htmlStr - - def htmlHashTagSwarm(baseDir: str, actor: str, translate: {}) -> str: """Returns a tag swarm of today's hashtags """ @@ -244,7 +189,6 @@ def htmlHashTagSwarm(baseDir: str, actor: str, translate: {}) -> str: getContentWarningButton('alltags', translate, tagSwarmStr) tagSwarmHtml = categorySwarmStr + tagSwarmStr.strip() + '\n' - # tagSwarmHtml += _getHashtagDomainHistogram(domainHistogram, translate) return tagSwarmHtml @@ -279,10 +223,11 @@ def htmlSearchHashtagCategory(cssCache: {}, translate: {}, htmlStr += '\n' - htmlStr += '
' - htmlStr += '



' - htmlStr += '

' - htmlStr += translate['Category'] + ': ' + categoryStr + '

' + htmlStr += \ + '' + htmlStr += \ + '
' + \ + '
' htmlStr += htmlFooter() return htmlStr diff --git a/webapp_headerbuttons.py b/webapp_headerbuttons.py index 99ab01290..a50e01da6 100644 --- a/webapp_headerbuttons.py +++ b/webapp_headerbuttons.py @@ -54,8 +54,7 @@ def headerButtonsTimeline(defaultTimeline: str, # first button if defaultTimeline == 'tlmedia': tlStr += \ - '' # add other buttons diff --git a/webapp_login.py b/webapp_login.py index 72a0f3846..45de12739 100644 --- a/webapp_login.py +++ b/webapp_login.py @@ -36,18 +36,19 @@ def htmlGetLoginCredentials(loginParams: str, password = None register = False for arg in loginArgs: - if '=' in arg: - if arg.split('=', 1)[0] == 'username': - nickname = arg.split('=', 1)[1] - if nickname.startswith('@'): - nickname = nickname[1:] - if '@' in nickname: - # the full nickname@domain has been entered - nickname = nickname.split('@')[0] - elif arg.split('=', 1)[0] == 'password': - password = arg.split('=', 1)[1] - elif arg.split('=', 1)[0] == 'register': - register = True + if '=' not in arg: + continue + if arg.split('=', 1)[0] == 'username': + nickname = arg.split('=', 1)[1] + if nickname.startswith('@'): + nickname = nickname[1:] + if '@' in nickname: + # the full nickname@domain has been entered + nickname = nickname.split('@')[0] + elif arg.split('=', 1)[0] == 'password': + password = arg.split('=', 1)[1] + elif arg.split('=', 1)[0] == 'register': + register = True return nickname, password, register @@ -103,8 +104,7 @@ def htmlLogin(cssCache: {}, translate: {}, else: loginText = \ '

' + \ - translate['Please enter some credentials'] + '

' - loginText += \ + translate['Please enter some credentials'] + '

' + \ '

' + \ translate['You will become the admin of this site.'] + \ '

' @@ -132,8 +132,7 @@ def htmlLogin(cssCache: {}, translate: {}, TOSstr = \ '

' + \ - translate['About this Instance'] + '

' - TOSstr += \ + translate['About this Instance'] + '

' + \ '

' + \ translate['Terms of Service'] + '

' @@ -153,34 +152,32 @@ def htmlLogin(cssCache: {}, translate: {}, htmlHeaderWithWebsiteMarkup(cssFilename, instanceTitle, httpPrefix, domain, systemLanguage) - loginForm += '
\n' - loginForm += '
\n' - loginForm += '
\n' instanceTitle = getConfigParam(baseDir, 'instanceTitle') - loginForm += textModeLogoHtml + '\n' loginForm += \ + '
\n' + \ + '\n' + \ + '
\n' + \ + textModeLogoHtml + '\n' + \ ' ' + instanceTitle + '\n' - loginForm += loginText + TOSstr + '\n' - loginForm += '
\n' - loginForm += '\n' - loginForm += '
\n' - loginForm += ' \n' - loginForm += \ + '" alt="' + instanceTitle + '" class="loginimage">\n' + \ + loginText + TOSstr + '\n' + \ + '
\n' + \ + '\n' + \ + '
\n' + \ + ' \n' + \ ' \n' - loginForm += '\n' - loginForm += ' \n' - loginForm += \ + translate['Enter Nickname'] + \ + '" name="username" required autofocus>\n' + \ + '\n' + \ + ' \n' + \ ' \n' - loginForm += loginButtonStr + registerButtonStr + '\n' - loginForm += '
\n' - loginForm += '\n' - loginForm += \ + '" name="password" required>\n' + \ + loginButtonStr + registerButtonStr + '\n' + \ + '
\n' + \ + '\n' + \ '' + \ '' + \
diff --git a/webapp_media.py b/webapp_media.py
index 4d5ff6366..5ef899fe0 100644
--- a/webapp_media.py
+++ b/webapp_media.py
@@ -8,6 +8,7 @@ __status__ = None: @@ -110,6 +111,8 @@ def _addEmbeddedVideoFromSites(translate: {}, content: str, if '"https://' in content: if peertubeInstances: + # only create an embedded video for a limited set of + # peertube sites. peerTubeSites = peertubeInstances else: # A default selection of the current larger peertube sites, @@ -160,19 +163,21 @@ def _addEmbeddedVideoFromSites(translate: {}, content: str, else: siteStr = 'https://' + site siteStr = '"' + siteStr - if siteStr in content: - url = content.split(siteStr)[1] - if '"' in url: - url = url.split('"')[0].replace('/watch/', '/embed/') - content = \ - content + "
\n\n
\n" - return content + if siteStr not in content: + continue + url = content.split(siteStr)[1] + if '"' not in url: + continue + url = url.split('"')[0].replace('/watch/', '/embed/') + content = \ + content + "
\n\n
\n" + return content return content @@ -205,19 +210,14 @@ def _addEmbeddedAudio(translate: {}, content: str) -> str: if not w.endswith(extension): continue - if not (w.startswith('http') or w.startswith('dat:') or - w.startswith('hyper:') or w.startswith('i2p:') or - w.startswith('gnunet:') or - '/' in w): + if not validUrlPrefix(w): continue - url = w - content += '
\n\n
\n' + '
\n\n
\n' return content @@ -251,23 +251,17 @@ def _addEmbeddedVideo(translate: {}, content: str) -> str: w = w[:-1] if not w.endswith(extension): continue - if not (w.startswith('http') or w.startswith('dat:') or - w.startswith('hyper:') or w.startswith('i2p:') or - w.startswith('gnunet:') or - '/' in w): + if not validUrlPrefix(w): continue - url = w content += \ '
\n' + \ ' \n
\n
\n' + 'preload="metadata">\n' + \ + '\n' + \ + translate['Your browser does not support the video element.'] + \ + '\n\n\n' return content diff --git a/webapp_person_options.py b/webapp_person_options.py index 6b55aafd8..3c3309196 100644 --- a/webapp_person_options.py +++ b/webapp_person_options.py @@ -398,9 +398,10 @@ def htmlPersonOptions(defaultTimeline: str, 'accesskey="' + accessKeys['enterNotes'] + '">' + \ personNotes + '\n' - optionsStr += ' \n' - optionsStr += '\n' - optionsStr += '\n' - optionsStr += '\n' + optionsStr += \ + ' \n' + \ + '\n' + \ + '\n' + \ + '\n' optionsStr += htmlFooter() return optionsStr diff --git a/webapp_post.py b/webapp_post.py index fa1317344..3d3ff9941 100644 --- a/webapp_post.py +++ b/webapp_post.py @@ -22,6 +22,7 @@ from posts import postIsMuted from posts import getPersonBox from posts import downloadAnnounce from posts import populateRepliesJson +from utils import hasObjectDict from utils import updateAnnounceCollection from utils import isPGPEncrypted from utils import isDM @@ -840,8 +841,7 @@ def _getPostTitleAnnounceHtml(baseDir: str, postJsonObject) else: titleStr += \ - _announceUnattributedHtml(translate, - postJsonObject) + _announceUnattributedHtml(translate, postJsonObject) else: titleStr += \ _announceUnattributedHtml(translate, postJsonObject) @@ -1339,7 +1339,7 @@ def individualPostAsHtml(allowDownloads: bool, _logPostTiming(enableTimingLog, postStartTime, '8') - if not isinstance(postJsonObject['object'], dict): + if not hasObjectDict(postJsonObject): return '' # if this post should be public then check its recipients @@ -1762,7 +1762,7 @@ def htmlIndividualPost(cssCache: {}, messageId = removeIdEnding(postJsonObject['id']) # show the previous posts - if isinstance(postJsonObject['object'], dict): + if hasObjectDict(postJsonObject): while postJsonObject['object'].get('inReplyTo'): postFilename = \ locatePost(baseDir, nickname, domain, diff --git a/webapp_profile.py b/webapp_profile.py index eced71548..3439b834b 100644 --- a/webapp_profile.py +++ b/webapp_profile.py @@ -1601,64 +1601,66 @@ def _htmlEditProfileFiltering(baseDir: str, nickname: str, domain: str, def _htmlEditProfileChangePassword(translate: {}) -> str: """Change password section of edit profile screen """ - editProfileForm = '
' + \ - translate['Change Password'] + '\n' - editProfileForm += '
\n' - editProfileForm += \ + editProfileForm = \ + '
' + \ + translate['Change Password'] + '\n' + \ + '
\n' + \ '
\n' - editProfileForm += '
\n' - editProfileForm += \ + '
\n' + \ + '
\n' + \ '
\n' - editProfileForm += \ - ' \n' - editProfileForm += '
\n' + '
\n' + \ + ' \n' + \ + '
\n' return editProfileForm def _htmlEditProfileBackground(newsInstance: bool, translate: {}) -> str: """Background images section of edit profile screen """ - editProfileForm = '
' + \ - translate['Background Images'] + '\n' - editProfileForm += '
\n' idx = 'The files attached below should be no larger than ' + \ '10MB in total uploaded at once.' - editProfileForm += \ + editProfileForm = \ + '
' + \ + translate['Background Images'] + '\n' + \ + '
\n' + \ '

\n' if not newsInstance: imageFormats = getImageFormats() editProfileForm += \ ' \n' - editProfileForm += ' \n' + translate['Background image'] + '\n' + \ + ' \n' - editProfileForm += '
\n' - editProfileForm += ' \n' + editProfileForm += \ + '
\n' + \ + ' \n' - editProfileForm += '
\n' - editProfileForm += ' \n' + editProfileForm += \ + '
\n' + \ + ' \n' - editProfileForm += '
\n' - editProfileForm += ' \n' + editProfileForm += \ + '
\n' + \ + ' \n' - editProfileForm += '
\n' - editProfileForm += ' \n' + editProfileForm += \ + '
\n' + \ + ' \n' editProfileForm += '
\n' return editProfileForm @@ -1696,48 +1698,45 @@ def _htmlEditProfileContactInfo(nickname: str, ' \n' - editProfileForm += '
\n' editProfileForm += \ + '
\n' + \ ' \n' - editProfileForm += '
\n' editProfileForm += \ + '
\n' + \ ' \n' - editProfileForm += '
\n' editProfileForm += \ + '
\n' + \ ' \n' - editProfileForm += '
\n' editProfileForm += \ + '
\n' + \ ' \n' - editProfileForm += '
\n' editProfileForm += \ + '
\n' + \ ' \n' editProfileForm += \ '
\n' - editProfileForm += \ + translate['PGP Fingerprint'] + '
\n' + \ ' \n' - editProfileForm += \ - '
\n' - editProfileForm += \ + PGPfingerprint + '">\n' + \ + '
\n' + \ ' \n' - editProfileForm += '

\n' - editProfileForm += '
\n' + translate['Following'] + '
\n' + \ + ' \n' return editProfileForm @@ -1790,30 +1789,32 @@ def _htmlEditProfileMain(displayNickname: str, bioStr: str, imageFormats = getImageFormats() editProfileForm = '
\n' - editProfileForm += ' \n' + editProfileForm += \ + ' \n' + \ '
\n' + editProfileForm += \ - ' \n' - editProfileForm += \ + ' \n' + \ ' \n' + editProfileForm += \ ' \n' - editProfileForm += \ - ' \n' + '\n' + \ + ' \n' occupationName = '' if actorJson.get('hasOccupation'): occupationName = getOccupationName(actorJson) - editProfileForm += '
\n' editProfileForm += \ + '
\n' + \ ' \n' @@ -1827,27 +1828,29 @@ def _htmlEditProfileMain(displayNickname: str, bioStr: str, ctr += 1 alsoKnownAsStr += altActor - editProfileForm += '
\n' editProfileForm += \ + '
\n' + \ ' \n' - editProfileForm += '
\n' editProfileForm += \ + '
\n' + \ ' \n' - editProfileForm += '
\n' + editProfileForm += \ + '
\n' + \ ' \n' - editProfileForm += '
\n' + editProfileForm += \ + '
\n' + \ ' \n' - editProfileForm += '
\n' + blogAddress + '">\n' + \ + ' \n' return editProfileForm @@ -1858,8 +1861,8 @@ def _htmlEditProfileTopBanner(baseDir: str, """top banner on edit profile screen """ editProfileForm = \ - '' - editProfileForm += '' + \ + '\n' editProfileForm += \ @@ -2157,7 +2160,7 @@ def _individualFollowAsHtml(translate: {}, '?options=' + followUrl + \ ';1;' + avatarUrl + '">\n' - if b == 'unfollow': + elif b == 'unfollow': buttonsStr += \ 'Account Suspended

\n' - suspendedForm += '

See Terms of Service

\n' - suspendedForm += '\n' + suspendedForm += \ + '
\n' + \ + '

Account Suspended

\n' + \ + '

See Terms of Service

\n' + \ + '
\n' suspendedForm += htmlFooter() return suspendedForm diff --git a/webapp_timeline.py b/webapp_timeline.py index 2ba8e6881..3ab1800fd 100644 --- a/webapp_timeline.py +++ b/webapp_timeline.py @@ -600,14 +600,15 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str, iconsAsButtons, accessKeys) # start the timeline - tlStr += '\n' - tlStr += ' \n' - tlStr += ' \n' - tlStr += ' \n' - tlStr += ' \n' - tlStr += ' \n' - tlStr += ' \n' - tlStr += ' \n' + tlStr += \ + '
\n' + \ + ' \n' + \ + ' \n' + \ + ' \n' + \ + ' \n' + \ + ' \n' + \ + ' \n' + \ + ' \n' domainFull = getFullDomain(domain, port) diff --git a/webapp_utils.py b/webapp_utils.py index 5bf49b8ce..336eea016 100644 --- a/webapp_utils.py +++ b/webapp_utils.py @@ -452,17 +452,18 @@ def getRightImageFile(baseDir: str, def htmlHeaderWithExternalStyle(cssFilename: str, instanceTitle: str, lang='en') -> str: - htmlStr = '\n' - htmlStr += '\n' - htmlStr += ' \n' - htmlStr += ' \n' cssFile = '/' + cssFilename.split('/')[-1] - htmlStr += ' \n' - htmlStr += ' \n' - htmlStr += ' \n' - htmlStr += ' ' + instanceTitle + '\n' - htmlStr += ' \n' - htmlStr += ' \n' + htmlStr = \ + '\n' + \ + '\n' + \ + ' \n' + \ + ' \n' + \ + ' \n' + \ + ' \n' + \ + ' \n' + \ + ' ' + instanceTitle + '\n' + \ + ' \n' + \ + ' \n' return htmlStr @@ -511,37 +512,32 @@ def htmlHeaderWithPersonMarkup(cssFilename: str, instanceTitle: str, sk['occupationalCategory']['codeValue'] categoryUrl = \ 'https://www.onetonline.org/link/summary/' + category - skillsMarkup += ' {\n' - skillsMarkup += ' "@type": "Role",\n' - skillsMarkup += ' "hasOccupation": {\n' - skillsMarkup += ' "@type": "Occupation",\n' - skillsMarkup += ' "name": "' + roleName + '",\n' - skillsMarkup += ' "description": ' + \ - '"Fediverse instance role",\n' - skillsMarkup += ' "occupationLocation": {\n' - skillsMarkup += \ - ' "@type": "City",\n' - skillsMarkup += \ - ' "name": "' + city + '"\n' - skillsMarkup += ' },\n' - skillsMarkup += ' "occupationalCategory": {\n' - skillsMarkup += ' "@type": "CategoryCode",\n' - skillsMarkup += ' "inCodeSet": {\n' - skillsMarkup += \ - ' "@type": "CategoryCodeSet",\n' - skillsMarkup += ' "name": "O*Net-SOC",\n' - skillsMarkup += ' "dateModified": "2019",\n' skillsMarkup += \ + ' {\n' + \ + ' "@type": "Role",\n' + \ + ' "hasOccupation": {\n' + \ + ' "@type": "Occupation",\n' + \ + ' "name": "' + roleName + '",\n' + \ + ' "description": ' + \ + '"Fediverse instance role",\n' + \ + ' "occupationLocation": {\n' + \ + ' "@type": "City",\n' + \ + ' "name": "' + city + '"\n' + \ + ' },\n' + \ + ' "occupationalCategory": {\n' + \ + ' "@type": "CategoryCode",\n' + \ + ' "inCodeSet": {\n' + \ + ' "@type": "CategoryCodeSet",\n' + \ + ' "name": "O*Net-SOC",\n' + \ + ' "dateModified": "2019",\n' + \ ' ' + \ - '"url": "https://www.onetonline.org/"\n' - skillsMarkup += ' },\n' - skillsMarkup += \ - ' "codeValue": "' + category + '",\n' - skillsMarkup += \ - ' "url": "' + categoryUrl + '"\n' - skillsMarkup += ' }\n' - skillsMarkup += ' }\n' - skillsMarkup += ' }' + '"url": "https://www.onetonline.org/"\n' + \ + ' },\n' + \ + ' "codeValue": "' + category + '",\n' + \ + ' "url": "' + categoryUrl + '"\n' + \ + ' }\n' + \ + ' }\n' + \ + ' }' elif skillDict['@type'] == 'Occupation': if not firstEntry: skillsMarkup += ',\n' @@ -555,19 +551,18 @@ def htmlHeaderWithPersonMarkup(cssFilename: str, instanceTitle: str, skillsListStr += ', ' skillsListStr += '"' + skillStr + '"' skillsListStr += ']' - skillsMarkup += ' {\n' - skillsMarkup += ' "@type": "Occupation",\n' - skillsMarkup += ' "name": "' + ocName + '",\n' - skillsMarkup += ' "description": ' + \ - '"Fediverse instance occupation",\n' - skillsMarkup += ' "occupationLocation": {\n' - skillsMarkup += ' "@type": "City",\n' skillsMarkup += \ - ' "name": "' + city + '"\n' - skillsMarkup += ' },\n' - skillsMarkup += \ - ' "skills": ' + skillsListStr + '\n' - skillsMarkup += ' }' + ' {\n' + \ + ' "@type": "Occupation",\n' + \ + ' "name": "' + ocName + '",\n' + \ + ' "description": ' + \ + '"Fediverse instance occupation",\n' + \ + ' "occupationLocation": {\n' + \ + ' "@type": "City",\n' + \ + ' "name": "' + city + '"\n' + \ + ' },\n' + \ + ' "skills": ' + skillsListStr + '\n' + \ + ' }' firstEntry = False skillsMarkup += '\n ],\n' diff --git a/webapp_welcome_final.py b/webapp_welcome_final.py index 0b6491c47..de5940199 100644 --- a/webapp_welcome_final.py +++ b/webapp_welcome_final.py @@ -63,19 +63,17 @@ def htmlWelcomeFinal(baseDir: str, nickname: str, domain: str, finalForm = htmlHeaderWithExternalStyle(cssFilename, instanceTitle) - finalForm += '
' + finalText + '
\n' finalForm += \ + '
' + finalText + '
\n' + \ '\n' - finalForm += '\n' finalForm += '\n' finalForm += htmlFooter() diff --git a/webfinger.py b/webfinger.py index c82a5199c..6a31c5833 100644 --- a/webfinger.py +++ b/webfinger.py @@ -174,18 +174,19 @@ def webfingerNodeInfo(httpPrefix: str, domainFull: str) -> {}: def webfingerMeta(httpPrefix: str, domainFull: str) -> str: """Return /.well-known/host-meta """ - metaStr = "" - metaStr += "" - metaStr += "" - metaStr += "" - metaStr += " Resource Descriptor" - metaStr += " " - metaStr += "" + metaStr = \ + "" + \ + "" + \ + "" + \ + "" + domainFull + "" + \ + "" + \ + "" + \ + " Resource Descriptor" + \ + " " + \ + "" return metaStr @@ -264,18 +265,28 @@ def _webfingerUpdateFromProfile(wfJson: {}, actorJson: {}) -> bool: "matrix": "matrix", "email": "mailto", "ssb": "ssb", + "briar": "briar", + "cwtch": "cwtch", + "jami": "jami", "tox": "toxId" } + aliasesNotFound = [] + for name, alias in webfingerPropertyName.items(): + aliasesNotFound.append(alias) + for propertyValue in actorJson['attachment']: if not propertyValue.get('name'): continue propertyName = propertyValue['name'].lower() - if not (propertyName.startswith('ssb') or - propertyName.startswith('xmpp') or - propertyName.startswith('matrix') or - propertyName.startswith('email') or - propertyName.startswith('tox')): + found = False + for name, alias in webfingerPropertyName.items(): + if name == propertyName: + if alias in aliasesNotFound: + aliasesNotFound.remove(alias) + found = True + break + if not found: continue if not propertyValue.get('type'): continue @@ -285,6 +296,9 @@ def _webfingerUpdateFromProfile(wfJson: {}, actorJson: {}) -> bool: continue newValue = propertyValue['value'].strip() + if '://' in newValue: + newValue = newValue.split('://')[1] + aliasIndex = 0 found = False for alias in wfJson['aliases']: @@ -300,6 +314,17 @@ def _webfingerUpdateFromProfile(wfJson: {}, actorJson: {}) -> bool: else: wfJson['aliases'].append(newAlias) changed = True + + # remove any aliases which are no longer in the actor profile + removeAlias = [] + for alias in aliasesNotFound: + for fullAlias in wfJson['aliases']: + if fullAlias.startswith(alias + ':'): + removeAlias.append(fullAlias) + for fullAlias in removeAlias: + wfJson['aliases'].remove(fullAlias) + changed = True + return changed