mirror of https://gitlab.com/bashrc2/epicyon
Merge branch 'main' of ssh://code.freedombone.net:2222/bashrc/epicyon
commit
e5af948446
|
|
@ -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
|
||||
|
|
|
|||
2
inbox.py
2
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: ' +
|
||||
|
|
|
|||
3
media.py
3
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
|
||||
|
|
|
|||
6
posts.py
6
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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
16
theme.py
16
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,
|
||||
|
|
|
|||
14
utils.py
14
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
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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>'
|
||||
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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 = \
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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="' + \
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 + \
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
59
webfinger.py
59
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 = "<?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
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue