Merge branch 'main' of ssh://code.freedombone.net:2222/bashrc/epicyon

main
Bob Mottram 2021-07-06 15:38:32 +01:00
commit e5af948446
26 changed files with 391 additions and 468 deletions

View File

@ -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

View File

@ -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: ' +

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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 += '<main><table class="calendar">\n'
calendarStr += '<caption class="calendar__banner--month">\n'
@ -369,20 +368,10 @@ def htmlCalendar(personCache: {}, cssCache: {}, translate: {},
calendarStr += '</caption>\n'
calendarStr += '<thead>\n'
calendarStr += '<tr>\n'
calendarStr += ' <th scope="col" class="calendar__day__header">' + \
translate['Sun'] + '</th>\n'
calendarStr += ' <th scope="col" class="calendar__day__header">' + \
translate['Mon'] + '</th>\n'
calendarStr += ' <th scope="col" class="calendar__day__header">' + \
translate['Tue'] + '</th>\n'
calendarStr += ' <th scope="col" class="calendar__day__header">' + \
translate['Wed'] + '</th>\n'
calendarStr += ' <th scope="col" class="calendar__day__header">' + \
translate['Thu'] + '</th>\n'
calendarStr += ' <th scope="col" class="calendar__day__header">' + \
translate['Fri'] + '</th>\n'
calendarStr += ' <th scope="col" class="calendar__day__header">' + \
translate['Sat'] + '</th>\n'
days = ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat')
for d in days:
calendarStr += ' <th scope="col" class="calendar__day__header">' + \
translate[d] + '</th>\n'
calendarStr += '</tr>\n'
calendarStr += '</thead>\n'
calendarStr += '<tbody>\n'

View File

@ -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 <center>\n' + \
' <img class="leftColImg" ' + \
'\n <center>\n <img class="leftColImg" ' + \
'alt="" loading="lazy" src="/users/' + \
nickname + '/' + leftImageFile + '" />\n' + \
' </center>\n'
if showBackButton:
htmlStr += \
' <div>' + \
' <a href="' + timelinePath + '">' + \
' <div> <a href="' + timelinePath + '">' + \
'<button class="cancelbtn">' + \
translate['Go Back'] + '</button></a>\n'
@ -112,14 +108,11 @@ def getLeftColumnContent(baseDir: str, nickname: str, domainFull: str,
if editor:
# show the edit icon
htmlStr += \
' <a href="' + \
'/users/' + nickname + '/editlinks" ' + \
' <a href="/users/' + nickname + '/editlinks" ' + \
'accesskey="' + accessKeys['menuEdit'] + '">' + \
'<img class="' + editImageClass + \
'" loading="lazy" alt="' + \
'<img class="' + editImageClass + '" loading="lazy" alt="' + \
translate['Edit Links'] + ' | " title="' + \
translate['Edit Links'] + '" src="/' + \
'icons/edit.png" /></a>\n'
translate['Edit Links'] + '" src="/icons/edit.png" /></a>\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 = \
' <a href="' + rssUrl + '">' + \
'<img class="' + editImageClass + \
'" loading="lazy" alt="' + rssTitle + \
'" title="' + rssTitle + \
' <a href="' + rssUrl + '"><img class="' + editImageClass + \
'" loading="lazy" alt="' + rssTitle + '" title="' + rssTitle + \
'" src="/icons/logorss.png" /></a>\n'
if rssIconAtTop:
htmlStr += rssIconStr
@ -326,8 +317,7 @@ def htmlLinksMobile(cssCache: {}, baseDir: str,
theme, accessKeys)
else:
if editor:
htmlStr += '<br><br><br>\n'
htmlStr += '<center>\n '
htmlStr += '<br><br><br>\n<center>\n '
htmlStr += translate['Select the edit icon to add web links']
htmlStr += '\n</center>\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 += '<img loading="lazy" class="timeline-banner" ' + \
editLinksForm += \
'<img loading="lazy" class="timeline-banner" ' + \
'alt = "" src="' + \
'/users/' + nickname + '/' + bannerFile + '" /></a>\n' + \
'</header>\n'
@ -411,8 +402,7 @@ def htmlEditLinks(cssCache: {}, translate: {}, baseDir: str, path: str,
'<br>'
editLinksForm += \
' <textarea id="message" name="editedLinks" ' + \
'style="height:80vh" spellcheck="false">' + \
linksStr + '</textarea>'
'style="height:80vh" spellcheck="false">' + linksStr + '</textarea>'
editLinksForm += \
'</div>'

View File

@ -253,8 +253,7 @@ def _htmlNewswire(baseDir: str, newswire: {}, nickname: str, moderator: bool,
if faviconUrl:
faviconLink = \
'<img loading="lazy" src="' + faviconUrl + '" ' + \
'alt="" ' + \
_getBrokenFavSubstitute() + '/>'
'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,
'<a href="' + url + '" target="_blank" ' + \
'rel="nofollow noopener noreferrer">' + \
'<span class="newswireItemVotedOn">' + \
faviconLink + title + \
'</span></a>' + totalVotesStr
faviconLink + title + '</span></a>' + totalVotesStr
if moderator:
htmlStr += \
' ' + dateShown + '<a href="/users/' + nickname + \
@ -312,8 +310,7 @@ def _htmlNewswire(baseDir: str, newswire: {}, nickname: str, moderator: bool,
htmlStr += '<p class="newswireItem">' + \
'<a href="' + url + '" target="_blank" ' + \
'rel="nofollow noopener noreferrer">' + \
faviconLink + title + '</a>' + \
totalVotesStr
faviconLink + title + '</a>' + totalVotesStr
htmlStr += ' <span class="newswireDate">'
htmlStr += dateShown + '</span></p>\n'

View File

@ -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 += '<div class="follow">\n'
sharesStr += ' <div class="followAvatar">\n'
@ -186,8 +185,7 @@ def htmlConfirmFollow(cssCache: {}, translate: {}, baseDir: str,
if os.path.isfile(baseDir + '/follow.css'):
cssFilename = baseDir + '/follow.css'
instanceTitle = \
getConfigParam(baseDir, 'instanceTitle')
instanceTitle = getConfigParam(baseDir, 'instanceTitle')
followStr = htmlHeaderWithExternalStyle(cssFilename, instanceTitle)
followStr += '<div class="follow">\n'
followStr += ' <div class="followAvatar">\n'
@ -232,8 +230,7 @@ def htmlConfirmUnfollow(cssCache: {}, translate: {}, baseDir: str,
if os.path.isfile(baseDir + '/follow.css'):
cssFilename = baseDir + '/follow.css'
instanceTitle = \
getConfigParam(baseDir, 'instanceTitle')
instanceTitle = getConfigParam(baseDir, 'instanceTitle')
followStr = htmlHeaderWithExternalStyle(cssFilename, instanceTitle)
followStr += '<div class="follow">\n'
followStr += ' <div class="followAvatar">\n'
@ -279,8 +276,7 @@ def htmlConfirmUnblock(cssCache: {}, translate: {}, baseDir: str,
if os.path.isfile(baseDir + '/follow.css'):
cssFilename = baseDir + '/follow.css'
instanceTitle = \
getConfigParam(baseDir, 'instanceTitle')
instanceTitle = getConfigParam(baseDir, 'instanceTitle')
blockStr = htmlHeaderWithExternalStyle(cssFilename, instanceTitle)
blockStr += '<div class="block">\n'
blockStr += ' <div class="blockAvatar">\n'

View File

@ -26,35 +26,35 @@ def _htmlFollowingDataList(baseDir: str, nickname: str,
listStr = '<datalist id="followingHandles">\n'
followingFilename = \
baseDir + '/accounts/' + nickname + '@' + domain + '/following.txt'
msg = None
if os.path.isfile(followingFilename):
with open(followingFilename, 'r') as followingFile:
msg = followingFile.read()
# add your own handle, so that you can send DMs
# to yourself as reminders
msg += nickname + '@' + domainFull + '\n'
# include petnames
petnamesFilename = \
baseDir + '/accounts/' + \
nickname + '@' + domain + '/petnames.txt'
if os.path.isfile(petnamesFilename):
followingList = []
with open(petnamesFilename, 'r') as petnamesFile:
petStr = petnamesFile.read()
# extract each petname and append it
petnamesList = petStr.split('\n')
for pet in petnamesList:
followingList.append(pet.split(' ')[0])
# add the following.txt entries
followingList += msg.split('\n')
else:
# no petnames list exists - just use following.txt
followingList = msg.split('\n')
followingList.sort()
if followingList:
for followingAddress in followingList:
if followingAddress:
listStr += \
'<option>@' + followingAddress + '</option>\n'
if msg:
# include petnames
petnamesFilename = \
baseDir + '/accounts/' + nickname + '@' + domain + '/petnames.txt'
if os.path.isfile(petnamesFilename):
followingList = []
with open(petnamesFilename, 'r') as petnamesFile:
petStr = petnamesFile.read()
# extract each petname and append it
petnamesList = petStr.split('\n')
for pet in petnamesList:
followingList.append(pet.split(' ')[0])
# add the following.txt entries
followingList += msg.split('\n')
else:
# no petnames list exists - just use following.txt
followingList = msg.split('\n')
followingList.sort()
if followingList:
for followingAddress in followingList:
if followingAddress:
listStr += '<option>@' + followingAddress + '</option>\n'
listStr += '</datalist>\n'
return listStr
@ -84,8 +84,7 @@ def _htmlNewPostDropDown(scopeIcon: str, scopeDescription: str,
dropDownContent += ' <label for="my-newPostDropdown"\n'
dropDownContent += ' data-toggle="newPostDropdown">\n'
dropDownContent += ' <img loading="lazy" alt="" title="" src="/' + \
'icons/' + scopeIcon + '"/><b>' + \
scopeDescription + '</b></label>\n'
'icons/' + scopeIcon + '"/><b>' + scopeDescription + '</b></label>\n'
if noDropDown:
dropDownContent += '</div></nav>\n'
@ -144,12 +143,6 @@ def _htmlNewPostDropDown(scopeIcon: str, scopeDescription: str,
'icons/scope_reminder.png"/><b>' + \
translate['Reminder'] + '</b><br>' + \
translate['Scheduled note to yourself'] + '</a></li>\n'
# dropDownContent += \
# '<li><a href="' + pathBase + dropdownEventSuffix + \
# '"><img loading="lazy" alt="" title="" src="/' + \
# 'icons/scope_event.png"/><b>' + \
# translate['Event'] + '</b><br>' + \
# translate['Create an event'] + '</a></li>\n'
dropDownContent += \
'<li><a href="' + pathBase + dropdownReportSuffix + \
'" accesskey="' + accessKeys['reportButton'] + '">' + \
@ -230,8 +223,7 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {},
showPublicOnDropdown = False
else:
newPostText = \
'<h1>' + \
translate['Write your report below.'] + '</h1>\n'
'<h1>' + translate['Write your report below.'] + '</h1>\n'
# custom report header with any additional instructions
if os.path.isfile(baseDir + '/accounts/report.txt'):
@ -319,7 +311,6 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {},
translate['Subject or Content Warning (optional)'] + '...'
placeholderMentions = ''
if inReplyTo:
# mentionsAndContent = getMentionsString(content)
placeholderMentions = \
translate['Replying to'] + '...'
placeholderMessage = translate['Write something'] + '...'
@ -582,8 +573,7 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {},
dateAndLocation += '<input type="text" name="category">\n'
dateAndLocation += '</div>\n'
instanceTitle = \
getConfigParam(baseDir, 'instanceTitle')
instanceTitle = getConfigParam(baseDir, 'instanceTitle')
newPostForm = htmlHeaderWithExternalStyle(cssFilename, instanceTitle)
newPostForm += \
@ -711,17 +701,11 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {},
submitText + '" ' + \
'accesskey="' + accessKeys['submitButton'] + '"></td>\n'
newPostForm += ' </tr>\n'
newPostForm += '</table>\n'
newPostForm += ' </tr>\n</table>\n'
newPostForm += ' </div>\n'
newPostForm += ' <div class="containerSubmitNewPost"><center>\n'
# newPostForm += \
# ' <a href="' + pathBase + \
# '/inbox"><button class="cancelbtn">' + \
# translate['Go Back'] + '</button></a>\n'
newPostForm += ' </center></div>\n'
newPostForm += replyStr
@ -777,14 +761,13 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {},
if not mediaInstance or replyStr:
newPostForm += newPostImageSection
newPostForm += ' <div class="container">\n'
newPostForm += \
' <div class="container">\n' + \
' <input type="submit" name="submitPost" value="' + \
submitText + '">\n'
newPostForm += ' </div>\n'
newPostForm += ' </div>\n'
newPostForm += '</form>\n'
submitText + '">\n' + \
' </div>\n' + \
' </div>\n' + \
'</form>\n'
if not reportUrl:
newPostForm = \

View File

@ -128,22 +128,24 @@ def htmlFrontScreen(rssIconAtTop: bool,
if loginButton:
profileHeaderStr += '<center>' + loginButton + '</center>\n'
profileHeaderStr += '<table class="timeline">\n'
profileHeaderStr += ' <colgroup>\n'
profileHeaderStr += ' <col span="1" class="column-left">\n'
profileHeaderStr += ' <col span="1" class="column-center">\n'
profileHeaderStr += ' <col span="1" class="column-right">\n'
profileHeaderStr += ' </colgroup>\n'
profileHeaderStr += ' <tbody>\n'
profileHeaderStr += ' <tr>\n'
profileHeaderStr += ' <td valign="top" class="col-left">\n'
profileHeaderStr += \
'<table class="timeline">\n' + \
' <colgroup>\n' + \
' <col span="1" class="column-left">\n' + \
' <col span="1" class="column-center">\n' + \
' <col span="1" class="column-right">\n' + \
' </colgroup>\n' + \
' <tbody>\n' + \
' <tr>\n' + \
' <td valign="top" class="col-left">\n'
profileHeaderStr += \
getLeftColumnContent(baseDir, 'news', domainFull,
httpPrefix, translate,
False, False, None, rssIconAtTop, True,
True, theme, accessKeys)
profileHeaderStr += ' </td>\n'
profileHeaderStr += ' <td valign="top" class="col-center">\n'
profileHeaderStr += \
' </td>\n' + \
' <td valign="top" class="col-center">\n'
profileStr = profileHeaderStr
@ -177,10 +179,11 @@ def htmlFrontScreen(rssIconAtTop: bool,
False, None, False, False,
False, True, authorized, True, theme,
defaultTimeline, accessKeys)
profileFooterStr += ' </td>\n'
profileFooterStr += ' </tr>\n'
profileFooterStr += ' </tbody>\n'
profileFooterStr += '</table>\n'
profileFooterStr += \
' </td>\n' + \
' </tr>\n' + \
' </tbody>\n' + \
'</table>\n'
instanceTitle = \
getConfigParam(baseDir, 'instanceTitle')

View File

@ -29,17 +29,19 @@ def getHashtagCategoriesFeed(baseDir: str,
if not hashtagCategories:
return None
rssStr = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n"
rssStr += "<rss version=\"2.0\">\n"
rssStr += '<channel>\n'
rssStr += ' <title>#categories</title>\n'
rssStr = \
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" + \
"<rss version=\"2.0\">\n" + \
'<channel>\n' + \
' <title>#categories</title>\n'
rssDateStr = \
datetime.utcnow().strftime("%a, %d %b %Y %H:%M:%S UT")
for categoryStr, hashtagList in hashtagCategories.items():
rssStr += '<item>\n'
rssStr += ' <title>' + categoryStr + '</title>\n'
rssStr += \
'<item>\n' + \
' <title>' + categoryStr + '</title>\n'
listStr = ''
for hashtag in hashtagList:
if ':' in hashtag:
@ -47,75 +49,18 @@ def getHashtagCategoriesFeed(baseDir: str,
if '&' in hashtag:
continue
listStr += hashtag + ' '
rssStr += ' <description>' + listStr.strip() + '</description>\n'
rssStr += ' <link/>\n'
rssStr += ' <pubDate>' + rssDateStr + '</pubDate>\n'
rssStr += '</item>\n'
rssStr += \
' <description>' + listStr.strip() + '</description>\n' + \
' <link/>\n' + \
' <pubDate>' + rssDateStr + '</pubDate>\n' + \
'</item>\n'
rssStr += '</channel>\n'
rssStr += '</rss>\n'
rssStr += \
'</channel>\n' + \
'</rss>\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 = '<br><br><center>\n'
histogramHeaderStr += ' <h1>' + translate['Hashtag origins'] + '</h1>\n'
histogramHeaderStr += ' <table class="domainHistogram">\n'
histogramHeaderStr += ' <colgroup>\n'
histogramHeaderStr += ' <col span="1" class="domainHistogramLeft">\n'
histogramHeaderStr += ' <col span="1" class="domainHistogramRight">\n'
histogramHeaderStr += ' </colgroup>\n'
histogramHeaderStr += ' <tbody>\n'
histogramHeaderStr += ' <tr>\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) + '%<br>'
rightColStr += domain + '<br>'
del domainHistogram[domain]
if htmlStr:
htmlStr += ' <td>' + leftColStr + '</td>\n'
htmlStr += ' <td>' + rightColStr + '</td>\n'
htmlStr += ' </tr>\n'
htmlStr += ' </tbody>\n'
htmlStr += ' </table>\n'
htmlStr += '</center>\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 += '<img loading="lazy" class="timeline-banner" src="' + \
actor + '/' + searchBannerFile + '" alt="" /></a>\n'
htmlStr += '<div class="follow">'
htmlStr += '<center><br><br><br>'
htmlStr += '<h1><a href="' + actor + '/search"><b>'
htmlStr += translate['Category'] + ': ' + categoryStr + '</b></a></h1>'
htmlStr += \
'<div class="follow">' + \
'<center><br><br><br>' + \
'<h1><a href="' + actor + '/search"><b>' + \
translate['Category'] + ': ' + categoryStr + '</b></a></h1>'
hashtagsDict = getHashtagCategories(baseDir, True, categoryStr)
if hashtagsDict:
@ -293,7 +238,8 @@ def htmlSearchHashtagCategory(cssCache: {}, translate: {},
'<a href="' + actor + '/tags/' + tagName + \
'" class="hashtagswarm">' + tagName + '</a>\n'
htmlStr += '</center>'
htmlStr += '</div>'
htmlStr += \
'</center>' + \
'</div>'
htmlStr += htmlFooter()
return htmlStr

View File

@ -54,8 +54,7 @@ def headerButtonsTimeline(defaultTimeline: str,
# first button
if defaultTimeline == 'tlmedia':
tlStr += \
'<a href="' + usersPath + \
'/tlmedia" tabindex="-1" ' + \
'<a href="' + usersPath + '/tlmedia" tabindex="-1" ' + \
'accesskey="' + accessKeys['menuMedia'] + '"' + \
'><button class="' + \
mediaButton + '"><span>' + translate['Media'] + \
@ -105,8 +104,7 @@ def headerButtonsTimeline(defaultTimeline: str,
if defaultTimeline != 'tlmedia':
if not minimal and not featuresHeader:
tlStr += \
'<a href="' + usersPath + \
'/tlmedia" tabindex="-1" ' + \
'<a href="' + usersPath + '/tlmedia" tabindex="-1" ' + \
'accesskey="' + accessKeys['menuMedia'] + '">' + \
'<button class="' + \
mediaButton + '"><span>' + translate['Media'] + \
@ -206,10 +204,8 @@ def headerButtonsTimeline(defaultTimeline: str,
if not featuresHeader:
# button for the outbox
tlStr += \
'<a href="' + usersPath + \
'/outbox"><button class="' + \
sentButton + '" tabindex="-1">' + \
'<span>' + translate['Sent'] + \
'<a href="' + usersPath + '/outbox"><button class="' + \
sentButton + '" tabindex="-1"><span>' + translate['Sent'] + \
'</span></button></a>'
# add other buttons

View File

@ -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 = \
'<p class="login-text">' + \
translate['Please enter some credentials'] + '</p>'
loginText += \
translate['Please enter some credentials'] + '</p>' + \
'<p class="login-text">' + \
translate['You will become the admin of this site.'] + \
'</p>'
@ -132,8 +132,7 @@ def htmlLogin(cssCache: {}, translate: {},
TOSstr = \
'<p class="login-text"><a href="/about">' + \
translate['About this Instance'] + '</a></p>'
TOSstr += \
translate['About this Instance'] + '</a></p>' + \
'<p class="login-text"><a href="/terms">' + \
translate['Terms of Service'] + '</a></p>'
@ -153,34 +152,32 @@ def htmlLogin(cssCache: {}, translate: {},
htmlHeaderWithWebsiteMarkup(cssFilename, instanceTitle,
httpPrefix, domain,
systemLanguage)
loginForm += '<br>\n'
loginForm += '<form method="POST" action="/login">\n'
loginForm += ' <div class="imgcontainer">\n'
instanceTitle = getConfigParam(baseDir, 'instanceTitle')
loginForm += textModeLogoHtml + '\n'
loginForm += \
'<br>\n' + \
'<form method="POST" action="/login">\n' + \
' <div class="imgcontainer">\n' + \
textModeLogoHtml + '\n' + \
' <img loading="lazy" src="' + loginImage + \
'" alt="' + instanceTitle + '" class="loginimage">\n'
loginForm += loginText + TOSstr + '\n'
loginForm += ' </div>\n'
loginForm += '\n'
loginForm += ' <div class="container">\n'
loginForm += ' <label for="nickname"><b>' + \
translate['Nickname'] + '</b></label>\n'
loginForm += \
'" alt="' + instanceTitle + '" class="loginimage">\n' + \
loginText + TOSstr + '\n' + \
' </div>\n' + \
'\n' + \
' <div class="container">\n' + \
' <label for="nickname"><b>' + \
translate['Nickname'] + '</b></label>\n' + \
' <input type="text" ' + autocompleteStr + ' placeholder="' + \
translate['Enter Nickname'] + '" name="username" required autofocus>\n'
loginForm += '\n'
loginForm += ' <label for="password"><b>' + \
translate['Password'] + '</b></label>\n'
loginForm += \
translate['Enter Nickname'] + \
'" name="username" required autofocus>\n' + \
'\n' + \
' <label for="password"><b>' + \
translate['Password'] + '</b></label>\n' + \
' <input type="password" ' + autocompleteStr + \
' placeholder="' + translate['Enter Password'] + \
'" name="password" required>\n'
loginForm += loginButtonStr + registerButtonStr + '\n'
loginForm += ' </div>\n'
loginForm += '</form>\n'
loginForm += \
'" name="password" required>\n' + \
loginButtonStr + registerButtonStr + '\n' + \
' </div>\n' + \
'</form>\n' + \
'<a href="https://gitlab.com/bashrc2/epicyon">' + \
'<img loading="lazy" class="license" title="' + \
translate['Get the source code'] + '" alt="' + \

View File

@ -8,6 +8,7 @@ __status__ = "Production"
__module_group__ = "Timeline"
import os
from utils import validUrlPrefix
def loadPeertubeInstances(baseDir: str, peertubeInstances: []) -> 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 + "<center>\n<iframe loading=\"lazy\" " + \
"sandbox=\"allow-same-origin " + \
"allow-scripts\" src=\"https://" + \
site + url + "\" width=\"" + str(width) + \
"\" height=\"" + str(height) + \
"\" frameborder=\"0\" allow=\"autoplay; " + \
"fullscreen\" allowfullscreen></iframe>\n</center>\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 + "<center>\n<iframe loading=\"lazy\" " + \
"sandbox=\"allow-same-origin " + \
"allow-scripts\" src=\"https://" + \
site + url + "\" width=\"" + str(width) + \
"\" height=\"" + str(height) + \
"\" frameborder=\"0\" allow=\"autoplay; " + \
"fullscreen\" allowfullscreen></iframe>\n</center>\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 += '<center>\n<audio controls>\n'
content += \
'<source src="' + url + '" type="audio/' + \
extension.replace('.', '') + '">'
content += \
translate['Your browser does not support the audio element.']
content += '</audio>\n</center>\n'
'<center>\n<audio controls>\n' + \
'<source src="' + w + '" type="audio/' + \
extension.replace('.', '') + '">' + \
translate['Your browser does not support the audio element.'] + \
'</audio>\n</center>\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 += \
'<center><figure id="videoContainer" ' + \
'data-fullscreen="false">\n' + \
' <video id="video" controls ' + \
'preload="metadata">\n'
content += \
'<source src="' + url + '" type="video/' + \
extension.replace('.', '') + '">\n'
content += \
translate['Your browser does not support the video element.']
content += '</video>\n</figure>\n</center>\n'
'preload="metadata">\n' + \
'<source src="' + w + '" type="video/' + \
extension.replace('.', '') + '">\n' + \
translate['Your browser does not support the video element.'] + \
'</video>\n</figure>\n</center>\n'
return content

View File

@ -398,9 +398,10 @@ def htmlPersonOptions(defaultTimeline: str,
'accesskey="' + accessKeys['enterNotes'] + '">' + \
personNotes + '</textarea>\n'
optionsStr += ' </form>\n'
optionsStr += '</center>\n'
optionsStr += '</div>\n'
optionsStr += '</div>\n'
optionsStr += \
' </form>\n' + \
'</center>\n' + \
'</div>\n' + \
'</div>\n'
optionsStr += htmlFooter()
return optionsStr

View File

@ -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,

View File

@ -1601,64 +1601,66 @@ def _htmlEditProfileFiltering(baseDir: str, nickname: str, domain: str,
def _htmlEditProfileChangePassword(translate: {}) -> str:
"""Change password section of edit profile screen
"""
editProfileForm = ' <details><summary class="cw">' + \
translate['Change Password'] + '</summary>\n'
editProfileForm += ' <div class="container">\n'
editProfileForm += \
editProfileForm = \
' <details><summary class="cw">' + \
translate['Change Password'] + '</summary>\n' + \
' <div class="container">\n' + \
'<label class="labels">' + translate['Change Password'] + \
'</label><br>\n'
editProfileForm += ' <input type="text" name="password" ' + \
'value=""><br>\n'
editProfileForm += \
'</label><br>\n' + \
' <input type="text" name="password" ' + \
'value=""><br>\n' + \
'<label class="labels">' + translate['Confirm Password'] + \
'</label><br>\n'
editProfileForm += \
' <input type="text" name="passwordconfirm" value="">\n'
editProfileForm += ' </div></details>\n'
'</label><br>\n' + \
' <input type="text" name="passwordconfirm" value="">\n' + \
' </div></details>\n'
return editProfileForm
def _htmlEditProfileBackground(newsInstance: bool, translate: {}) -> str:
"""Background images section of edit profile screen
"""
editProfileForm = ' <details><summary class="cw">' + \
translate['Background Images'] + '</summary>\n'
editProfileForm += ' <div class="container">\n'
idx = 'The files attached below should be no larger than ' + \
'10MB in total uploaded at once.'
editProfileForm += \
editProfileForm = \
' <details><summary class="cw">' + \
translate['Background Images'] + '</summary>\n' + \
' <div class="container">\n' + \
' <label class="labels">' + translate[idx] + '</label><br><br>\n'
if not newsInstance:
imageFormats = getImageFormats()
editProfileForm += \
' <label class="labels">' + \
translate['Background image'] + '</label>\n'
editProfileForm += ' <input type="file" id="image" name="image"'
editProfileForm += ' accept="' + imageFormats + '">\n'
translate['Background image'] + '</label>\n' + \
' <input type="file" id="image" name="image"' + \
' accept="' + imageFormats + '">\n'
editProfileForm += ' <br><label class="labels">' + \
translate['Timeline banner image'] + '</label>\n'
editProfileForm += ' <input type="file" id="banner" name="banner"'
editProfileForm += ' accept="' + imageFormats + '">\n'
editProfileForm += \
' <br><label class="labels">' + \
translate['Timeline banner image'] + '</label>\n' + \
' <input type="file" id="banner" name="banner"' + \
' accept="' + imageFormats + '">\n'
editProfileForm += ' <br><label class="labels">' + \
translate['Search banner image'] + '</label>\n'
editProfileForm += ' <input type="file" id="search_banner" '
editProfileForm += 'name="search_banner"'
editProfileForm += ' accept="' + imageFormats + '">\n'
editProfileForm += \
' <br><label class="labels">' + \
translate['Search banner image'] + '</label>\n' + \
' <input type="file" id="search_banner" ' + \
'name="search_banner"' + \
' accept="' + imageFormats + '">\n'
editProfileForm += ' <br><label class="labels">' + \
translate['Left column image'] + '</label>\n'
editProfileForm += ' <input type="file" id="left_col_image" '
editProfileForm += 'name="left_col_image"'
editProfileForm += ' accept="' + imageFormats + '">\n'
editProfileForm += \
' <br><label class="labels">' + \
translate['Left column image'] + '</label>\n' + \
' <input type="file" id="left_col_image" ' + \
'name="left_col_image"' + \
' accept="' + imageFormats + '">\n'
editProfileForm += ' <br><label class="labels">' + \
translate['Right column image'] + '</label>\n'
editProfileForm += ' <input type="file" id="right_col_image" '
editProfileForm += 'name="right_col_image"'
editProfileForm += ' accept="' + imageFormats + '">\n'
editProfileForm += \
' <br><label class="labels">' + \
translate['Right column image'] + '</label>\n' + \
' <input type="file" id="right_col_image" ' + \
'name="right_col_image"' + \
' accept="' + imageFormats + '">\n'
editProfileForm += ' </div></details>\n'
return editProfileForm
@ -1696,48 +1698,45 @@ def _htmlEditProfileContactInfo(nickname: str,
' <input type="text" name="matrixAddress" value="' + \
matrixAddress + '">\n'
editProfileForm += '<label class="labels">SSB</label><br>\n'
editProfileForm += \
'<label class="labels">SSB</label><br>\n' + \
' <input type="text" name="ssbAddress" value="' + \
ssbAddress + '">\n'
editProfileForm += '<label class="labels">Tox</label><br>\n'
editProfileForm += \
'<label class="labels">Tox</label><br>\n' + \
' <input type="text" name="toxAddress" value="' + \
toxAddress + '">\n'
editProfileForm += '<label class="labels">Briar</label><br>\n'
editProfileForm += \
'<label class="labels">Briar</label><br>\n' + \
' <input type="text" name="briarAddress" value="' + \
briarAddress + '">\n'
editProfileForm += '<label class="labels">Jami</label><br>\n'
editProfileForm += \
'<label class="labels">Jami</label><br>\n' + \
' <input type="text" name="jamiAddress" value="' + \
jamiAddress + '">\n'
editProfileForm += '<label class="labels">Cwtch</label><br>\n'
editProfileForm += \
'<label class="labels">Cwtch</label><br>\n' + \
' <input type="text" name="cwtchAddress" value="' + \
cwtchAddress + '">\n'
editProfileForm += \
'<label class="labels">' + \
translate['PGP Fingerprint'] + '</label><br>\n'
editProfileForm += \
translate['PGP Fingerprint'] + '</label><br>\n' + \
' <input type="text" name="openpgp" value="' + \
PGPfingerprint + '">\n'
editProfileForm += \
'<label class="labels">' + translate['PGP'] + '</label><br>\n'
editProfileForm += \
PGPfingerprint + '">\n' + \
'<label class="labels">' + translate['PGP'] + '</label><br>\n' + \
' <textarea id="message" placeholder=' + \
'"-----BEGIN PGP PUBLIC KEY BLOCK-----" name="pgp" ' + \
'style="height:600px" spellcheck="false">' + \
PGPpubKey + '</textarea>\n'
editProfileForm += '<a href="/users/' + nickname + \
PGPpubKey + '</textarea>\n' + \
'<a href="/users/' + nickname + \
'/followingaccounts"><label class="labels">' + \
translate['Following'] + '</label></a><br>\n'
editProfileForm += ' </div></details>\n'
translate['Following'] + '</label></a><br>\n' + \
' </div></details>\n'
return editProfileForm
@ -1790,30 +1789,32 @@ def _htmlEditProfileMain(displayNickname: str, bioStr: str,
imageFormats = getImageFormats()
editProfileForm = ' <div class="container">\n'
editProfileForm += ' <label class="labels">' + \
translate['Nickname'] + '</label>\n'
editProfileForm += \
' <label class="labels">' + \
translate['Nickname'] + '</label>\n' + \
' <input type="text" name="displayNickname" value="' + \
displayNickname + '"><br>\n'
editProfileForm += \
' <label class="labels">' + translate['Your bio'] + '</label>\n'
editProfileForm += \
' <label class="labels">' + \
translate['Your bio'] + '</label>\n' + \
' <textarea id="message" name="bio" style="height:200px" ' + \
'spellcheck="true">' + bioStr + '</textarea>\n'
editProfileForm += \
' <label class="labels">' + translate['Avatar image'] + \
'</label>\n'
editProfileForm += \
' <input type="file" id="avatar" name="avatar"'
editProfileForm += ' accept="' + imageFormats + '">\n'
'</label>\n' + \
' <input type="file" id="avatar" name="avatar"' + \
' accept="' + imageFormats + '">\n'
occupationName = ''
if actorJson.get('hasOccupation'):
occupationName = getOccupationName(actorJson)
editProfileForm += '<label class="labels">' + \
translate['Occupation'] + ':</label><br>\n'
editProfileForm += \
'<label class="labels">' + \
translate['Occupation'] + ':</label><br>\n' + \
' <input type="text" ' + \
'name="occupationName" value="' + occupationName + '">\n'
@ -1827,27 +1828,29 @@ def _htmlEditProfileMain(displayNickname: str, bioStr: str,
ctr += 1
alsoKnownAsStr += altActor
editProfileForm += '<label class="labels">' + \
translate['Other accounts'] + ':</label><br>\n'
editProfileForm += \
'<label class="labels">' + \
translate['Other accounts'] + ':</label><br>\n' + \
' <input type="text" placeholder="https://..." ' + \
'name="alsoKnownAs" value="' + alsoKnownAsStr + '">\n'
editProfileForm += '<label class="labels">' + \
translate['Moved to new account address'] + ':</label><br>\n'
editProfileForm += \
'<label class="labels">' + \
translate['Moved to new account address'] + ':</label><br>\n' + \
' <input type="text" placeholder="https://..." ' + \
'name="movedTo" value="' + movedTo + '">\n'
editProfileForm += '<label class="labels">' + \
translate['Donations link'] + '</label><br>\n'
editProfileForm += \
'<label class="labels">' + \
translate['Donations link'] + '</label><br>\n' + \
' <input type="text" placeholder="https://..." ' + \
'name="donateUrl" value="' + donateUrl + '">\n'
editProfileForm += '<label class="labels">Blog</label><br>\n'
editProfileForm += \
'<label class="labels">Blog</label><br>\n' + \
' <input type="text" name="blogAddress" value="' + \
blogAddress + '">\n'
editProfileForm += ' </div>\n'
blogAddress + '">\n' + \
' </div>\n'
return editProfileForm
@ -1858,8 +1861,8 @@ def _htmlEditProfileTopBanner(baseDir: str,
"""top banner on edit profile screen
"""
editProfileForm = \
'<a href="/users/' + nickname + '/' + defaultTimeline + '">'
editProfileForm += '<img loading="lazy" class="timeline-banner" src="' + \
'<a href="/users/' + nickname + '/' + defaultTimeline + '">' + \
'<img loading="lazy" class="timeline-banner" src="' + \
'/users/' + nickname + '/' + bannerFile + '" alt="" /></a>\n'
editProfileForm += \
@ -2157,7 +2160,7 @@ def _individualFollowAsHtml(translate: {},
'?options=' + followUrl + \
';1;' + avatarUrl + '"><button class="buttonunfollow">' + \
translate['Block'] + '</button></a>\n'
if b == 'unfollow':
elif b == 'unfollow':
buttonsStr += \
'<a href="/users/' + actorNickname + \
'?options=' + followUrl + \

View File

@ -338,8 +338,7 @@ def htmlSearch(cssCache: {}, translate: {},
if os.path.isfile(baseDir + '/search.css'):
cssFilename = baseDir + '/search.css'
instanceTitle = \
getConfigParam(baseDir, 'instanceTitle')
instanceTitle = getConfigParam(baseDir, 'instanceTitle')
followStr = htmlHeaderWithExternalStyle(cssFilename, instanceTitle)
# show a banner above the search box

View File

@ -24,9 +24,10 @@ def htmlSuspended(cssCache: {}, baseDir: str) -> str:
instanceTitle = \
getConfigParam(baseDir, 'instanceTitle')
suspendedForm = htmlHeaderWithExternalStyle(cssFilename, instanceTitle)
suspendedForm += '<div><center>\n'
suspendedForm += ' <p class="screentitle">Account Suspended</p>\n'
suspendedForm += ' <p>See <a href="/terms">Terms of Service</a></p>\n'
suspendedForm += '</center></div>\n'
suspendedForm += \
'<div><center>\n' + \
' <p class="screentitle">Account Suspended</p>\n' + \
' <p>See <a href="/terms">Terms of Service</a></p>\n' + \
'</center></div>\n'
suspendedForm += htmlFooter()
return suspendedForm

View File

@ -600,14 +600,15 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
iconsAsButtons, accessKeys)
# start the timeline
tlStr += '<table class="timeline">\n'
tlStr += ' <colgroup>\n'
tlStr += ' <col span="1" class="column-left">\n'
tlStr += ' <col span="1" class="column-center">\n'
tlStr += ' <col span="1" class="column-right">\n'
tlStr += ' </colgroup>\n'
tlStr += ' <tbody>\n'
tlStr += ' <tr>\n'
tlStr += \
'<table class="timeline">\n' + \
' <colgroup>\n' + \
' <col span="1" class="column-left">\n' + \
' <col span="1" class="column-center">\n' + \
' <col span="1" class="column-right">\n' + \
' </colgroup>\n' + \
' <tbody>\n' + \
' <tr>\n'
domainFull = getFullDomain(domain, port)

View File

@ -452,17 +452,18 @@ def getRightImageFile(baseDir: str,
def htmlHeaderWithExternalStyle(cssFilename: str, instanceTitle: str,
lang='en') -> str:
htmlStr = '<!DOCTYPE html>\n'
htmlStr += '<html lang="' + lang + '">\n'
htmlStr += ' <head>\n'
htmlStr += ' <meta charset="utf-8">\n'
cssFile = '/' + cssFilename.split('/')[-1]
htmlStr += ' <link rel="stylesheet" href="' + cssFile + '">\n'
htmlStr += ' <link rel="manifest" href="/manifest.json">\n'
htmlStr += ' <meta name="theme-color" content="grey">\n'
htmlStr += ' <title>' + instanceTitle + '</title>\n'
htmlStr += ' </head>\n'
htmlStr += ' <body>\n'
htmlStr = \
'<!DOCTYPE html>\n' + \
'<html lang="' + lang + '">\n' + \
' <head>\n' + \
' <meta charset="utf-8">\n' + \
' <link rel="stylesheet" href="' + cssFile + '">\n' + \
' <link rel="manifest" href="/manifest.json">\n' + \
' <meta name="theme-color" content="grey">\n' + \
' <title>' + instanceTitle + '</title>\n' + \
' </head>\n' + \
' <body>\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'

View File

@ -63,19 +63,17 @@ def htmlWelcomeFinal(baseDir: str, nickname: str, domain: str,
finalForm = htmlHeaderWithExternalStyle(cssFilename, instanceTitle)
finalForm += '<div class="container">' + finalText + '</div>\n'
finalForm += \
'<div class="container">' + finalText + '</div>\n' + \
'<form enctype="multipart/form-data" method="POST" ' + \
'accept-charset="UTF-8" ' + \
'action="/users/' + nickname + '/profiledata">\n'
finalForm += '<div class="container next">\n'
finalForm += \
'action="/users/' + nickname + '/profiledata">\n' + \
'<div class="container next">\n' + \
' <button type="submit" class="button" ' + \
'name="previewAvatar">' + translate['Go Back'] + '</button>\n'
finalForm += \
'name="previewAvatar">' + translate['Go Back'] + '</button>\n' + \
' <button type="submit" class="button" ' + \
'name="welcomeCompleteButton">' + translate['Next'] + '</button>\n'
finalForm += '</div>\n'
'name="welcomeCompleteButton">' + translate['Next'] + '</button>\n' + \
'</div>\n'
finalForm += '</form>\n'
finalForm += htmlFooter()

View File

@ -174,18 +174,19 @@ def webfingerNodeInfo(httpPrefix: str, domainFull: str) -> {}:
def webfingerMeta(httpPrefix: str, domainFull: str) -> str:
"""Return /.well-known/host-meta
"""
metaStr = "<?xml version=1.0' encoding=UTF-8'?>"
metaStr += "<XRD xmlns=http://docs.oasis-open.org/ns/xri/xrd-1.0'"
metaStr += " xmlns:hm=http://host-meta.net/xrd/1.0'>"
metaStr += ""
metaStr += "<hm:Host>" + domainFull + "</hm:Host>"
metaStr += ""
metaStr += "<Link rel=lrdd"
metaStr += " template=" + httpPrefix + "://" + domainFull + \
"/describe?uri={uri}'>"
metaStr += " <Title>Resource Descriptor</Title>"
metaStr += " </Link>"
metaStr += "</XRD>"
metaStr = \
"<?xml version=1.0' encoding=UTF-8'?>" + \
"<XRD xmlns=http://docs.oasis-open.org/ns/xri/xrd-1.0'" + \
" xmlns:hm=http://host-meta.net/xrd/1.0'>" + \
"" + \
"<hm:Host>" + domainFull + "</hm:Host>" + \
"" + \
"<Link rel=lrdd" + \
" template=" + httpPrefix + "://" + domainFull + \
"/describe?uri={uri}'>" + \
" <Title>Resource Descriptor</Title>" + \
" </Link>" + \
"</XRD>"
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