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

main
Bob Mottram 2021-06-25 19:20:11 +01:00
commit 40e766f029
43 changed files with 519 additions and 563 deletions

View File

@ -7,6 +7,7 @@ __email__ = "bob@freedombone.net"
__status__ = "Production" __status__ = "Production"
__module_group__ = "ActivityPub" __module_group__ = "ActivityPub"
from domainhandler import removeDomainPort
from utils import hasObjectDict from utils import hasObjectDict
from utils import removeIdEnding from utils import removeIdEnding
from utils import hasUsersPath from utils import hasUsersPath
@ -128,8 +129,7 @@ def createAnnounce(session, baseDir: str, federationList: [],
if not urlPermitted(objectUrl, federationList): if not urlPermitted(objectUrl, federationList):
return None return None
if ':' in domain: domain = removeDomainPort(domain)
domain = domain.split(':')[0]
fullDomain = getFullDomain(domain, port) fullDomain = getFullDomain(domain, port)
statusNumber, published = getStatusNumber() statusNumber, published = getStatusNumber()
@ -399,8 +399,7 @@ def outboxUndoAnnounce(recentPostsCache: {},
print('DEBUG: c2s undo announce request arrived in outbox') print('DEBUG: c2s undo announce request arrived in outbox')
messageId = removeIdEnding(messageJson['object']['object']) messageId = removeIdEnding(messageJson['object']['object'])
if ':' in domain: domain = removeDomainPort(domain)
domain = domain.split(':')[0]
postFilename = locatePost(baseDir, nickname, domain, messageId) postFilename = locatePost(baseDir, nickname, domain, messageId)
if not postFilename: if not postFilename:
if debug: if debug:

View File

@ -5,12 +5,13 @@ __version__ = "1.2.0"
__maintainer__ = "Bob Mottram" __maintainer__ = "Bob Mottram"
__email__ = "bob@freedombone.net" __email__ = "bob@freedombone.net"
__status__ = "Production" __status__ = "Production"
__module_group__ = "ActivityPub" __module_group__ = "Core"
import os import os
import json import json
import time import time
from datetime import datetime from datetime import datetime
from domainhandler import removeDomainPort
from utils import hasObjectDict from utils import hasObjectDict
from utils import isAccountDir from utils import isAccountDir
from utils import getCachedPostFilename from utils import getCachedPostFilename
@ -58,8 +59,7 @@ def addBlock(baseDir: str, nickname: str, domain: str,
blockNickname: str, blockDomain: str) -> bool: blockNickname: str, blockDomain: str) -> bool:
"""Block the given account """Block the given account
""" """
if ':' in domain: domain = removeDomainPort(domain)
domain = domain.split(':')[0]
blockingFilename = baseDir + '/accounts/' + \ blockingFilename = baseDir + '/accounts/' + \
nickname + '@' + domain + '/blocking.txt' nickname + '@' + domain + '/blocking.txt'
blockHandle = blockNickname + '@' + blockDomain blockHandle = blockNickname + '@' + blockDomain
@ -111,8 +111,7 @@ def removeBlock(baseDir: str, nickname: str, domain: str,
unblockNickname: str, unblockDomain: str) -> bool: unblockNickname: str, unblockDomain: str) -> bool:
"""Unblock the given account """Unblock the given account
""" """
if ':' in domain: domain = removeDomainPort(domain)
domain = domain.split(':')[0]
unblockingFilename = baseDir + '/accounts/' + \ unblockingFilename = baseDir + '/accounts/' + \
nickname + '@' + domain + '/blocking.txt' nickname + '@' + domain + '/blocking.txt'
unblockHandle = unblockNickname + '@' + unblockDomain unblockHandle = unblockNickname + '@' + unblockDomain
@ -338,8 +337,7 @@ def outboxBlock(baseDir: str, httpPrefix: str,
if debug: if debug:
print('DEBUG: c2s block object has no nickname') print('DEBUG: c2s block object has no nickname')
return return
if ':' in domain: domain = removeDomainPort(domain)
domain = domain.split(':')[0]
postFilename = locatePost(baseDir, nickname, domain, messageId) postFilename = locatePost(baseDir, nickname, domain, messageId)
if not postFilename: if not postFilename:
if debug: if debug:
@ -406,8 +404,7 @@ def outboxUndoBlock(baseDir: str, httpPrefix: str,
if debug: if debug:
print('DEBUG: c2s undo block object has no nickname') print('DEBUG: c2s undo block object has no nickname')
return return
if ':' in domain: domain = removeDomainPort(domain)
domain = domain.split(':')[0]
postFilename = locatePost(baseDir, nickname, domain, messageId) postFilename = locatePost(baseDir, nickname, domain, messageId)
if not postFilename: if not postFilename:
if debug: if debug:
@ -605,8 +602,7 @@ def outboxMute(baseDir: str, httpPrefix: str,
if debug: if debug:
print('DEBUG: c2s mute object has no nickname') print('DEBUG: c2s mute object has no nickname')
return return
if ':' in domain: domain = removeDomainPort(domain)
domain = domain.split(':')[0]
postFilename = locatePost(baseDir, nickname, domain, messageId) postFilename = locatePost(baseDir, nickname, domain, messageId)
if not postFilename: if not postFilename:
if debug: if debug:
@ -663,8 +659,7 @@ def outboxUndoMute(baseDir: str, httpPrefix: str,
if debug: if debug:
print('DEBUG: c2s undo mute object has no nickname') print('DEBUG: c2s undo mute object has no nickname')
return return
if ':' in domain: domain = removeDomainPort(domain)
domain = domain.split(':')[0]
postFilename = locatePost(baseDir, nickname, domain, messageId) postFilename = locatePost(baseDir, nickname, domain, messageId)
if not postFilename: if not postFilename:
if debug: if debug:

19
blog.py
View File

@ -16,6 +16,7 @@ from webapp_utils import htmlHeaderWithBlogMarkup
from webapp_utils import htmlFooter from webapp_utils import htmlFooter
from webapp_utils import getPostAttachmentsAsHtml from webapp_utils import getPostAttachmentsAsHtml
from webapp_media import addEmbeddedElements from webapp_media import addEmbeddedElements
from utils import isAccountDir
from utils import removeHtml from utils import removeHtml
from utils import getConfigParam from utils import getConfigParam
from utils import getFullDomain from utils import getFullDomain
@ -643,11 +644,7 @@ def _noOfBlogAccounts(baseDir: str) -> int:
ctr = 0 ctr = 0
for subdir, dirs, files in os.walk(baseDir + '/accounts'): for subdir, dirs, files in os.walk(baseDir + '/accounts'):
for acct in dirs: for acct in dirs:
if '@' not in acct: if not isAccountDir(acct):
continue
if acct.startswith('inbox@'):
continue
elif acct.startswith('news@'):
continue continue
accountDir = os.path.join(baseDir + '/accounts', acct) accountDir = os.path.join(baseDir + '/accounts', acct)
blogsIndex = accountDir + '/tlblogs.index' blogsIndex = accountDir + '/tlblogs.index'
@ -662,11 +659,7 @@ def _singleBlogAccountNickname(baseDir: str) -> str:
""" """
for subdir, dirs, files in os.walk(baseDir + '/accounts'): for subdir, dirs, files in os.walk(baseDir + '/accounts'):
for acct in dirs: for acct in dirs:
if '@' not in acct: if not isAccountDir(acct):
continue
if acct.startswith('inbox@'):
continue
elif acct.startswith('news@'):
continue continue
accountDir = os.path.join(baseDir + '/accounts', acct) accountDir = os.path.join(baseDir + '/accounts', acct)
blogsIndex = accountDir + '/tlblogs.index' blogsIndex = accountDir + '/tlblogs.index'
@ -704,11 +697,7 @@ def htmlBlogView(authorized: bool,
for subdir, dirs, files in os.walk(baseDir + '/accounts'): for subdir, dirs, files in os.walk(baseDir + '/accounts'):
for acct in dirs: for acct in dirs:
if '@' not in acct: if not isAccountDir(acct):
continue
if acct.startswith('inbox@'):
continue
elif acct.startswith('news@'):
continue continue
accountDir = os.path.join(baseDir + '/accounts', acct) accountDir = os.path.join(baseDir + '/accounts', acct)
blogsIndex = accountDir + '/tlblogs.index' blogsIndex = accountDir + '/tlblogs.index'

View File

@ -11,6 +11,7 @@ import os
from pprint import pprint from pprint import pprint
from webfinger import webfingerHandle from webfinger import webfingerHandle
from auth import createBasicAuthHeader from auth import createBasicAuthHeader
from domainhandler import removeDomainPort
from utils import hasUsersPath from utils import hasUsersPath
from utils import getFullDomain from utils import getFullDomain
from utils import removeIdEnding from utils import removeIdEnding
@ -560,8 +561,7 @@ def outboxBookmark(recentPostsCache: {},
print('DEBUG: c2s bookmark Add request arrived in outbox') print('DEBUG: c2s bookmark Add request arrived in outbox')
messageUrl = removeIdEnding(messageJson['object']['url']) messageUrl = removeIdEnding(messageJson['object']['url'])
if ':' in domain: domain = removeDomainPort(domain)
domain = domain.split(':')[0]
postFilename = locatePost(baseDir, nickname, domain, messageUrl) postFilename = locatePost(baseDir, nickname, domain, messageUrl)
if not postFilename: if not postFilename:
if debug: if debug:
@ -625,8 +625,7 @@ def outboxUndoBookmark(recentPostsCache: {},
print('DEBUG: c2s unbookmark Remove request arrived in outbox') print('DEBUG: c2s unbookmark Remove request arrived in outbox')
messageUrl = removeIdEnding(messageJson['object']['url']) messageUrl = removeIdEnding(messageJson['object']['url'])
if ':' in domain: domain = removeDomainPort(domain)
domain = domain.split(':')[0]
postFilename = locatePost(baseDir, nickname, domain, messageUrl) postFilename = locatePost(baseDir, nickname, domain, messageUrl)
if not postFilename: if not postFilename:
if debug: if debug:

View File

@ -5,11 +5,13 @@ __version__ = "1.2.0"
__maintainer__ = "Bob Mottram" __maintainer__ = "Bob Mottram"
__email__ = "bob@freedombone.net" __email__ = "bob@freedombone.net"
__status__ = "Production" __status__ = "Production"
__module_group__ = "Core"
import os import os
import email.parser import email.parser
import urllib.parse import urllib.parse
from shutil import copyfile from shutil import copyfile
from domainhandler import removeDomainPort
from utils import isValidLanguage from utils import isValidLanguage
from utils import getImageExtensions from utils import getImageExtensions
from utils import loadJson from utils import loadJson
@ -772,8 +774,7 @@ def addHtmlTags(baseDir: str, httpPrefix: str,
replaceEmoji = {} replaceEmoji = {}
emojiDict = {} emojiDict = {}
originalDomain = domain originalDomain = domain
if ':' in domain: domain = removeDomainPort(domain)
domain = domain.split(':')[0]
followingFilename = baseDir + '/accounts/' + \ followingFilename = baseDir + '/accounts/' + \
nickname + '@' + domain + '/following.txt' nickname + '@' + domain + '/following.txt'

View File

@ -5,6 +5,7 @@ __version__ = "1.2.0"
__maintainer__ = "Bob Mottram" __maintainer__ = "Bob Mottram"
__email__ = "bob@freedombone.net" __email__ = "bob@freedombone.net"
__status__ = "Production" __status__ = "Production"
__module_group__ = "Core"
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer, HTTPServer from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer, HTTPServer
import sys import sys
@ -139,8 +140,8 @@ from blog import htmlBlogView
from blog import htmlBlogPage from blog import htmlBlogPage
from blog import htmlBlogPost from blog import htmlBlogPost
from blog import htmlEditBlog from blog import htmlEditBlog
from webapp_utils import setMinimal from webapp_minimalbutton import setMinimal
from webapp_utils import isMinimal from webapp_minimalbutton import isMinimal
from webapp_utils import getAvatarImageUrl from webapp_utils import getAvatarImageUrl
from webapp_utils import htmlHashtagBlocked from webapp_utils import htmlHashtagBlocked
from webapp_utils import htmlFollowingList from webapp_utils import htmlFollowingList

View File

@ -9,6 +9,7 @@ __module_group__ = "ActivityPub"
import os import os
from datetime import datetime from datetime import datetime
from domainhandler import removeDomainPort
from utils import hasUsersPath from utils import hasUsersPath
from utils import getFullDomain from utils import getFullDomain
from utils import removeIdEnding from utils import removeIdEnding
@ -154,8 +155,7 @@ def outboxDelete(baseDir: str, httpPrefix: str,
"wasn't created by you (nickname does not match)") "wasn't created by you (nickname does not match)")
return return
deleteDomain, deletePort = getDomainFromActor(messageId) deleteDomain, deletePort = getDomainFromActor(messageId)
if ':' in domain: domain = removeDomainPort(domain)
domain = domain.split(':')[0]
if deleteDomain != domain: if deleteDomain != domain:
if debug: if debug:
print("DEBUG: you can't delete a post which " + print("DEBUG: you can't delete a post which " +

32
domainhandler.py 100644
View File

@ -0,0 +1,32 @@
__filename__ = "domainhandler.py"
__author__ = "Bob Mottram"
__license__ = "AGPL3+"
__version__ = "1.2.0"
__maintainer__ = "Bob Mottram"
__email__ = "bob@freedombone.net"
__status__ = "Production"
__module_group__ = "Core"
def removeDomainPort(domain: str) -> str:
"""If the domain has a port appended then remove it
eg. mydomain.com:80 becomes mydomain.com
"""
if ':' in domain:
if domain.startswith('did:'):
return domain
domain = domain.split(':')[0]
return domain
def getPortFromDomain(domain: str) -> int:
"""If the domain has a port number appended then return it
eg. mydomain.com:80 returns 80
"""
if ':' in domain:
if domain.startswith('did:'):
return None
portStr = domain.split(':')[1]
if portStr.isdigit():
return int(portStr)
return None

View File

@ -61,6 +61,8 @@ from tests import testUpdateActor
from tests import runAllTests from tests import runAllTests
from auth import storeBasicCredentials from auth import storeBasicCredentials
from auth import createPassword from auth import createPassword
from domainhandler import removeDomainPort
from domainhandler import getPortFromDomain
from utils import hasUsersPath from utils import hasUsersPath
from utils import getFullDomain from utils import getFullDomain
from utils import setConfigParam from utils import setConfigParam
@ -1080,8 +1082,8 @@ if args.message:
toDomain = toDomain.replace('\n', '').replace('\r', '') toDomain = toDomain.replace('\n', '').replace('\r', '')
toPort = 443 toPort = 443
if ':' in toDomain: if ':' in toDomain:
toPort = toDomain.split(':')[1] toPort = getPortFromDomain(toDomain)
toDomain = toDomain.split(':')[0] toDomain = removeDomainPort(toDomain)
else: else:
if args.sendto.endswith('followers'): if args.sendto.endswith('followers'):
toNickname = None toNickname = None

View File

@ -9,6 +9,7 @@ __module_group__ = "ActivityPub"
from pprint import pprint from pprint import pprint
import os import os
from domainhandler import removeDomainPort
from utils import hasObjectDict from utils import hasObjectDict
from utils import hasUsersPath from utils import hasUsersPath
from utils import getFullDomain from utils import getFullDomain
@ -153,8 +154,7 @@ def isFollowingActor(baseDir: str,
"""Is the given nickname following the given actor? """Is the given nickname following the given actor?
The actor can also be a handle: nickname@domain The actor can also be a handle: nickname@domain
""" """
if ':' in domain: domain = removeDomainPort(domain)
domain = domain.split(':')[0]
handle = nickname + '@' + domain handle = nickname + '@' + domain
if not os.path.isdir(baseDir + '/accounts/' + handle): if not os.path.isdir(baseDir + '/accounts/' + handle):
return False return False
@ -205,8 +205,7 @@ def isFollowerOfPerson(baseDir: str, nickname: str, domain: str,
followerNickname: str, followerDomain: str) -> bool: followerNickname: str, followerDomain: str) -> bool:
"""is the given nickname a follower of followerNickname? """is the given nickname a follower of followerNickname?
""" """
if ':' in domain: domain = removeDomainPort(domain)
domain = domain.split(':')[0]
followersFile = baseDir + '/accounts/' + \ followersFile = baseDir + '/accounts/' + \
nickname + '@' + domain + '/followers.txt' nickname + '@' + domain + '/followers.txt'
if not os.path.isfile(followersFile): if not os.path.isfile(followersFile):
@ -243,8 +242,7 @@ def unfollowAccount(baseDir: str, nickname: str, domain: str,
debug: bool = False) -> bool: debug: bool = False) -> bool:
"""Removes a person to the follow list """Removes a person to the follow list
""" """
if ':' in domain: domain = removeDomainPort(domain)
domain = domain.split(':')[0]
handle = nickname + '@' + domain handle = nickname + '@' + domain
handleToUnfollow = followNickname + '@' + followDomain handleToUnfollow = followNickname + '@' + followDomain
if not os.path.isdir(baseDir + '/accounts'): if not os.path.isdir(baseDir + '/accounts'):
@ -433,8 +431,7 @@ def getFollowingFeed(baseDir: str, domain: str, port: int, path: str,
} }
handleDomain = domain handleDomain = domain
if ':' in handleDomain: handleDomain = removeDomainPort(handleDomain)
handleDomain = domain.split(':')[0]
handle = nickname + '@' + handleDomain handle = nickname + '@' + handleDomain
filename = baseDir + '/accounts/' + handle + '/' + followFile + '.txt' filename = baseDir + '/accounts/' + handle + '/' + followFile + '.txt'
if not os.path.isfile(filename): if not os.path.isfile(filename):
@ -493,8 +490,7 @@ def _followApprovalRequired(baseDir: str, nicknameToFollow: str,
return False return False
manuallyApproveFollows = False manuallyApproveFollows = False
if ':' in domainToFollow: domainToFollow = removeDomainPort(domainToFollow)
domainToFollow = domainToFollow.split(':')[0]
actorFilename = baseDir + '/accounts/' + \ actorFilename = baseDir + '/accounts/' + \
nicknameToFollow + '@' + domainToFollow + '.json' nicknameToFollow + '@' + domainToFollow + '.json'
if os.path.isfile(actorFilename): if os.path.isfile(actorFilename):

View File

@ -8,6 +8,7 @@ __status__ = "Production"
__module_group__ = "Calendar" __module_group__ = "Calendar"
import os import os
from domainhandler import removeDomainPort
def receivingCalendarEvents(baseDir: str, nickname: str, domain: str, def receivingCalendarEvents(baseDir: str, nickname: str, domain: str,
@ -43,8 +44,7 @@ def _receiveCalendarEvents(baseDir: str, nickname: str, domain: str,
indicating whether to receive calendar events from that account indicating whether to receive calendar events from that account
""" """
# check that a following file exists # check that a following file exists
if ':' in domain: domain = removeDomainPort(domain)
domain = domain.split(':')[0]
followingFilename = baseDir + '/accounts/' + \ followingFilename = baseDir + '/accounts/' + \
nickname + '@' + domain + '/following.txt' nickname + '@' + domain + '/following.txt'
if not os.path.isfile(followingFilename): if not os.path.isfile(followingFilename):

2
git.py
View File

@ -5,7 +5,7 @@ __version__ = "1.2.0"
__maintainer__ = "Bob Mottram" __maintainer__ = "Bob Mottram"
__email__ = "bob@freedombone.net" __email__ = "bob@freedombone.net"
__status__ = "Production" __status__ = "Production"
__module_group__ = "ActivityPub" __module_group__ = "Core"
import os import os
import html import html

View File

@ -5,7 +5,7 @@ __version__ = "1.2.0"
__maintainer__ = "Bob Mottram" __maintainer__ = "Bob Mottram"
__email__ = "bob@freedombone.net" __email__ = "bob@freedombone.net"
__status__ = "Production" __status__ = "Production"
__module_group__ = "Calendar" __module_group__ = "Core"
import os import os
from uuid import UUID from uuid import UUID

View File

@ -13,6 +13,8 @@ import datetime
import time import time
import random import random
from linked_data_sig import verifyJsonSignature from linked_data_sig import verifyJsonSignature
from domainhandler import removeDomainPort
from domainhandler import getPortFromDomain
from utils import hasObjectDict from utils import hasObjectDict
from utils import dmAllowedFromDomain from utils import dmAllowedFromDomain
from utils import isRecentPost from utils import isRecentPost
@ -186,8 +188,7 @@ def _inboxStorePostToHtmlCache(recentPostsCache: {}, maxRecentPosts: int,
def validInbox(baseDir: str, nickname: str, domain: str) -> bool: def validInbox(baseDir: str, nickname: str, domain: str) -> bool:
"""Checks whether files were correctly saved to the inbox """Checks whether files were correctly saved to the inbox
""" """
if ':' in domain: domain = removeDomainPort(domain)
domain = domain.split(':')[0]
inboxDir = baseDir + '/accounts/' + nickname + '@' + domain + '/inbox' inboxDir = baseDir + '/accounts/' + nickname + '@' + domain + '/inbox'
if not os.path.isdir(inboxDir): if not os.path.isdir(inboxDir):
return True return True
@ -209,8 +210,7 @@ def validInboxFilenames(baseDir: str, nickname: str, domain: str,
"""Used by unit tests to check that the port number gets appended to """Used by unit tests to check that the port number gets appended to
domain names within saved post filenames domain names within saved post filenames
""" """
if ':' in domain: domain = removeDomainPort(domain)
domain = domain.split(':')[0]
inboxDir = baseDir + '/accounts/' + nickname + '@' + domain + '/inbox' inboxDir = baseDir + '/accounts/' + nickname + '@' + domain + '/inbox'
if not os.path.isdir(inboxDir): if not os.path.isdir(inboxDir):
return True return True
@ -358,8 +358,7 @@ def savePostToInboxQueue(baseDir: str, httpPrefix: str,
str(len(messageBytes)) + ' bytes') str(len(messageBytes)) + ' bytes')
return None return None
originalDomain = domain originalDomain = domain
if ':' in domain: domain = removeDomainPort(domain)
domain = domain.split(':')[0]
# block at the ealiest stage possible, which means the data # block at the ealiest stage possible, which means the data
# isn't written to file # isn't written to file
@ -536,8 +535,7 @@ def _inboxPostRecipients(baseDir: str, postJsonObject: {},
print('WARNING: inbox post has no actor') print('WARNING: inbox post has no actor')
return recipientsDict, recipientsDictFollowers return recipientsDict, recipientsDictFollowers
if ':' in domain: domain = removeDomainPort(domain)
domain = domain.split(':')[0]
domainBase = domain domainBase = domain
domain = getFullDomain(domain, port) domain = getFullDomain(domain, port)
domainMatch = '/' + domain + '/users/' domainMatch = '/' + domain + '/users/'
@ -1124,8 +1122,7 @@ def _receiveBookmark(recentPostsCache: {},
print('DEBUG: c2s inbox bookmark Add request arrived in outbox') print('DEBUG: c2s inbox bookmark Add request arrived in outbox')
messageUrl = removeIdEnding(messageJson['object']['url']) messageUrl = removeIdEnding(messageJson['object']['url'])
if ':' in domain: domain = removeDomainPort(domain)
domain = domain.split(':')[0]
postFilename = locatePost(baseDir, nickname, domain, messageUrl) postFilename = locatePost(baseDir, nickname, domain, messageUrl)
if not postFilename: if not postFilename:
if debug: if debug:
@ -1200,8 +1197,7 @@ def _receiveUndoBookmark(recentPostsCache: {},
'request arrived in outbox') 'request arrived in outbox')
messageUrl = removeIdEnding(messageJson['object']['url']) messageUrl = removeIdEnding(messageJson['object']['url'])
if ':' in domain: domain = removeDomainPort(domain)
domain = domain.split(':')[0]
postFilename = locatePost(baseDir, nickname, domain, messageUrl) postFilename = locatePost(baseDir, nickname, domain, messageUrl)
if not postFilename: if not postFilename:
if debug: if debug:
@ -1521,11 +1517,15 @@ def jsonPostAllowsComments(postJsonObject: {}) -> bool:
""" """
if 'commentsEnabled' in postJsonObject: if 'commentsEnabled' in postJsonObject:
return postJsonObject['commentsEnabled'] return postJsonObject['commentsEnabled']
if 'rejectReplies' in postJsonObject:
return not postJsonObject['rejectReplies']
if postJsonObject.get('object'): if postJsonObject.get('object'):
if not hasObjectDict(postJsonObject): if not hasObjectDict(postJsonObject):
return False return False
elif 'commentsEnabled' in postJsonObject['object']: elif 'commentsEnabled' in postJsonObject['object']:
return postJsonObject['object']['commentsEnabled'] return postJsonObject['object']['commentsEnabled']
elif 'rejectReplies' in postJsonObject['object']:
return not postJsonObject['object']['rejectReplies']
return True return True
@ -1580,24 +1580,6 @@ def populateReplies(baseDir: str, httpPrefix: str, domain: str,
print('DEBUG: post may have expired - ' + replyTo) print('DEBUG: post may have expired - ' + replyTo)
return False return False
# TODO store replies collection
# replyItem = {
# "type": "Document",
# "url": replyTo
# }
# if not messageJson['object'].get('replies'):
# messageJson['object']['replies'] = {
# "items": [replyItem]
# }
# else:
# found = False
# for item in messageJson['object']['replies']['items']:
# if item['url'] == replyTo:
# found = True
# break
# if not found:
# messageJson['object']['replies']['items'].append(replyItem)
#
if not _postAllowsComments(postFilename): if not _postAllowsComments(postFilename):
if debug: if debug:
print('DEBUG: post does not allow comments - ' + replyTo) print('DEBUG: post does not allow comments - ' + replyTo)
@ -1975,8 +1957,7 @@ def _sendToGroupMembers(session, baseDir: str, handle: str, port: int,
# set subject # set subject
if not postJsonObject['object'].get('summary'): if not postJsonObject['object'].get('summary'):
postJsonObject['object']['summary'] = 'General Discussion' postJsonObject['object']['summary'] = 'General Discussion'
if ':' in domain: domain = removeDomainPort(domain)
domain = domain.split(':')[0]
with open(followersFile, 'r') as groupMembers: with open(followersFile, 'r') as groupMembers:
for memberHandle in groupMembers: for memberHandle in groupMembers:
if memberHandle != handle: if memberHandle != handle:
@ -1984,10 +1965,8 @@ def _sendToGroupMembers(session, baseDir: str, handle: str, port: int,
memberDomain = memberHandle.split('@')[1] memberDomain = memberHandle.split('@')[1]
memberPort = port memberPort = port
if ':' in memberDomain: if ':' in memberDomain:
memberPortStr = memberDomain.split(':')[1] memberPort = getPortFromDomain(memberDomain)
if memberPortStr.isdigit(): memberDomain = removeDomainPort(memberDomain)
memberPort = int(memberPortStr)
memberDomain = memberDomain.split(':')[0]
sendSignedJson(postJsonObject, session, baseDir, sendSignedJson(postJsonObject, session, baseDir,
nickname, domain, port, nickname, domain, port,
memberNickname, memberDomain, memberPort, cc, memberNickname, memberDomain, memberPort, cc,
@ -2078,8 +2057,7 @@ def _updateLastSeen(baseDir: str, handle: str, actor: str) -> None:
return return
nickname = handle.split('@')[0] nickname = handle.split('@')[0]
domain = handle.split('@')[1] domain = handle.split('@')[1]
if ':' in domain: domain = removeDomainPort(domain)
domain = domain.split(':')[0]
accountPath = baseDir + '/accounts/' + nickname + '@' + domain accountPath = baseDir + '/accounts/' + nickname + '@' + domain
if not os.path.isdir(accountPath): if not os.path.isdir(accountPath):
return return
@ -2130,10 +2108,8 @@ def _bounceDM(senderPostId: str, session, httpPrefix: str,
senderDomain = sendingHandle.split('@')[1] senderDomain = sendingHandle.split('@')[1]
senderPort = port senderPort = port
if ':' in senderDomain: if ':' in senderDomain:
senderPortStr = senderDomain.split(':')[1] senderPort = getPortFromDomain(senderDomain)
if senderPortStr.isdigit(): senderDomain = removeDomainPort(senderDomain)
senderPort = int(senderPortStr)
senderDomain = senderDomain.split(':')[0]
cc = [] cc = []
# create the bounce DM # create the bounce DM

View File

@ -7,6 +7,7 @@ __email__ = "bob@freedombone.net"
__status__ = "Production" __status__ = "Production"
__module_group__ = "ActivityPub" __module_group__ = "ActivityPub"
from domainhandler import removeDomainPort
from utils import hasObjectDict from utils import hasObjectDict
from utils import hasUsersPath from utils import hasUsersPath
from utils import getFullDomain from utils import getFullDomain
@ -327,8 +328,7 @@ def outboxLike(recentPostsCache: {},
print('DEBUG: c2s like request arrived in outbox') print('DEBUG: c2s like request arrived in outbox')
messageId = removeIdEnding(messageJson['object']) messageId = removeIdEnding(messageJson['object'])
if ':' in domain: domain = removeDomainPort(domain)
domain = domain.split(':')[0]
postFilename = locatePost(baseDir, nickname, domain, messageId) postFilename = locatePost(baseDir, nickname, domain, messageId)
if not postFilename: if not postFilename:
if debug: if debug:
@ -377,8 +377,7 @@ def outboxUndoLike(recentPostsCache: {},
print('DEBUG: c2s undo like request arrived in outbox') print('DEBUG: c2s undo like request arrived in outbox')
messageId = removeIdEnding(messageJson['object']['object']) messageId = removeIdEnding(messageJson['object']['object'])
if ':' in domain: domain = removeDomainPort(domain)
domain = domain.split(':')[0]
postFilename = locatePost(baseDir, nickname, domain, messageId) postFilename = locatePost(baseDir, nickname, domain, messageId)
if not postFilename: if not postFilename:
if debug: if debug:

View File

@ -12,6 +12,8 @@ from follow import followedAccountAccepts
from follow import followedAccountRejects from follow import followedAccountRejects
from follow import removeFromFollowRequests from follow import removeFromFollowRequests
from utils import loadJson from utils import loadJson
from domainhandler import removeDomainPort
from domainhandler import getPortFromDomain
def manualDenyFollowRequest(session, baseDir: str, def manualDenyFollowRequest(session, baseDir: str,
@ -49,8 +51,8 @@ def manualDenyFollowRequest(session, baseDir: str,
denyHandle.split('@')[1].replace('\n', '').replace('\r', '') denyHandle.split('@')[1].replace('\n', '').replace('\r', '')
denyPort = port denyPort = port
if ':' in denyDomain: if ':' in denyDomain:
denyPort = denyDomain.split(':')[1] denyPort = getPortFromDomain(denyDomain)
denyDomain = denyDomain.split(':')[0] denyDomain = removeDomainPort(denyDomain)
followedAccountRejects(session, baseDir, httpPrefix, followedAccountRejects(session, baseDir, httpPrefix,
nickname, domain, port, nickname, domain, port,
denyNickname, denyDomain, denyPort, denyNickname, denyDomain, denyPort,
@ -141,9 +143,7 @@ def manualApproveFollowRequest(session, baseDir: str,
handleOfFollowRequester.replace('\r', '') handleOfFollowRequester.replace('\r', '')
port2 = port port2 = port
if ':' in handleOfFollowRequester: if ':' in handleOfFollowRequester:
port2Str = handleOfFollowRequester.split(':')[1] port2 = getPortFromDomain(handleOfFollowRequester)
if port2Str.isdigit():
port2 = int(port2Str)
requestsDir = accountDir + '/requests' requestsDir = accountDir + '/requests'
followActivityfilename = \ followActivityfilename = \
requestsDir + '/' + handleOfFollowRequester + '.follow' requestsDir + '/' + handleOfFollowRequester + '.follow'
@ -158,8 +158,8 @@ def manualApproveFollowRequest(session, baseDir: str,
approveDomain.replace('\r', '') approveDomain.replace('\r', '')
approvePort = port2 approvePort = port2
if ':' in approveDomain: if ':' in approveDomain:
approvePort = approveDomain.split(':')[1] approvePort = getPortFromDomain(approveDomain)
approveDomain = approveDomain.split(':')[0] approveDomain = removeDomainPort(approveDomain)
print('Manual follow accept: Sending Accept for ' + print('Manual follow accept: Sending Accept for ' +
handle + ' follow request from ' + handle + ' follow request from ' +
approveNickname + '@' + approveDomain) approveNickname + '@' + approveDomain)

168
markdown.py 100644
View File

@ -0,0 +1,168 @@
__filename__ = "markdown.py"
__author__ = "Bob Mottram"
__license__ = "AGPL3+"
__version__ = "1.2.0"
__maintainer__ = "Bob Mottram"
__email__ = "bob@freedombone.net"
__status__ = "Production"
__module_group__ = "Web Interface"
def _markdownEmphasisHtml(markdown: str) -> str:
"""Add italics and bold html markup to the given markdown
"""
replacements = {
' **': ' <b>',
'** ': '</b> ',
'**.': '</b>.',
'**:': '</b>:',
'**;': '</b>;',
'**,': '</b>,',
'**\n': '</b>\n',
' *': ' <i>',
'* ': '</i> ',
'*.': '</i>.',
'*:': '</i>:',
'*;': '</i>;',
'*,': '</i>,',
'*\n': '</i>\n',
' _': ' <ul>',
'_ ': '</ul> ',
'_.': '</ul>.',
'_:': '</ul>:',
'_;': '</ul>;',
'_,': '</ul>,',
'_\n': '</ul>\n'
}
for md, html in replacements.items():
markdown = markdown.replace(md, html)
if markdown.startswith('**'):
markdown = markdown[2:] + '<b>'
elif markdown.startswith('*'):
markdown = markdown[1:] + '<i>'
elif markdown.startswith('_'):
markdown = markdown[1:] + '<ul>'
if markdown.endswith('**'):
markdown = markdown[:len(markdown) - 2] + '</b>'
elif markdown.endswith('*'):
markdown = markdown[:len(markdown) - 1] + '</i>'
elif markdown.endswith('_'):
markdown = markdown[:len(markdown) - 1] + '</ul>'
return markdown
def _markdownReplaceQuotes(markdown: str) -> str:
"""Replaces > quotes with html blockquote
"""
if '> ' not in markdown:
return markdown
lines = markdown.split('\n')
result = ''
prevQuoteLine = None
for line in lines:
if '> ' not in line:
result += line + '\n'
prevQuoteLine = None
continue
lineStr = line.strip()
if not lineStr.startswith('> '):
result += line + '\n'
prevQuoteLine = None
continue
lineStr = lineStr.replace('> ', '', 1).strip()
if prevQuoteLine:
newPrevLine = prevQuoteLine.replace('</i></blockquote>\n', '')
result = result.replace(prevQuoteLine, newPrevLine) + ' '
lineStr += '</i></blockquote>\n'
else:
lineStr = '<blockquote><i>' + lineStr + '</i></blockquote>\n'
result += lineStr
prevQuoteLine = lineStr
if '</blockquote>\n' in result:
result = result.replace('</blockquote>\n', '</blockquote>')
if result.endswith('\n') and \
not markdown.endswith('\n'):
result = result[:len(result) - 1]
return result
def _markdownReplaceLinks(markdown: str, images: bool = False) -> str:
"""Replaces markdown links with html
Optionally replace image links
"""
replaceLinks = {}
text = markdown
startChars = '['
if images:
startChars = '!['
while startChars in text:
if ')' not in text:
break
text = text.split(startChars, 1)[1]
markdownLink = startChars + text.split(')')[0] + ')'
if ']' not in markdownLink or \
'(' not in markdownLink:
text = text.split(')', 1)[1]
continue
if not images:
replaceLinks[markdownLink] = \
'<a href="' + \
markdownLink.split('(')[1].split(')')[0] + \
'" target="_blank" rel="nofollow noopener noreferrer">' + \
markdownLink.split(startChars)[1].split(']')[0] + \
'</a>'
else:
replaceLinks[markdownLink] = \
'<img class="markdownImage" src="' + \
markdownLink.split('(')[1].split(')')[0] + \
'" alt="' + \
markdownLink.split(startChars)[1].split(']')[0] + \
'" />'
text = text.split(')', 1)[1]
for mdLink, htmlLink in replaceLinks.items():
markdown = markdown.replace(mdLink, htmlLink)
return markdown
def markdownToHtml(markdown: str) -> str:
"""Converts markdown formatted text to html
"""
markdown = _markdownReplaceQuotes(markdown)
markdown = _markdownEmphasisHtml(markdown)
markdown = _markdownReplaceLinks(markdown, True)
markdown = _markdownReplaceLinks(markdown)
# replace headers
linesList = markdown.split('\n')
htmlStr = ''
ctr = 0
for line in linesList:
if ctr > 0:
htmlStr += '<br>'
if line.startswith('#####'):
line = line.replace('#####', '').strip()
line = '<h5>' + line + '</h5>'
ctr = -1
elif line.startswith('####'):
line = line.replace('####', '').strip()
line = '<h4>' + line + '</h4>'
ctr = -1
elif line.startswith('###'):
line = line.replace('###', '').strip()
line = '<h3>' + line + '</h3>'
ctr = -1
elif line.startswith('##'):
line = line.replace('##', '').strip()
line = '<h2>' + line + '</h2>'
ctr = -1
elif line.startswith('#'):
line = line.replace('#', '').strip()
line = '<h1>' + line + '</h1>'
ctr = -1
htmlStr += line
ctr += 1
return htmlStr

View File

@ -5,7 +5,7 @@ __version__ = "1.2.0"
__maintainer__ = "Bob Mottram" __maintainer__ = "Bob Mottram"
__email__ = "bob@freedombone.net" __email__ = "bob@freedombone.net"
__status__ = "Production" __status__ = "Production"
__module_group__ = "Metadata" __module_group__ = "Core"
import os import os
from utils import loadJson from utils import loadJson

View File

@ -8,6 +8,7 @@ __status__ = "Production"
__module_group__ = "Core" __module_group__ = "Core"
import os import os
from utils import isAccountDir
from utils import getNicknameFromActor from utils import getNicknameFromActor
from utils import getDomainFromActor from utils import getDomainFromActor
from webfinger import webfingerHandle from webfinger import webfingerHandle
@ -186,11 +187,7 @@ def migrateAccounts(baseDir: str, session,
ctr = 0 ctr = 0
for subdir, dirs, files in os.walk(baseDir + '/accounts'): for subdir, dirs, files in os.walk(baseDir + '/accounts'):
for handle in dirs: for handle in dirs:
if '@' not in handle: if not isAccountDir(handle):
continue
if handle.startswith('inbox@'):
continue
if handle.startswith('news@'):
continue continue
nickname = handle.split('@')[0] nickname = handle.split('@')[0]
domain = handle.split('@')[1] domain = handle.split('@')[1]

View File

@ -38,6 +38,7 @@ from roles import setRole
from roles import setRolesFromList from roles import setRolesFromList
from roles import getActorRolesList from roles import getActorRolesList
from media import processMetaData from media import processMetaData
from domainhandler import removeDomainPort
from utils import getStatusNumber from utils import getStatusNumber
from utils import getFullDomain from utils import getFullDomain
from utils import validNickname from utils import validNickname
@ -48,10 +49,12 @@ from utils import getConfigParam
from utils import refreshNewswire from utils import refreshNewswire
from utils import getProtocolPrefixes from utils import getProtocolPrefixes
from utils import hasUsersPath from utils import hasUsersPath
from utils import getImageExtensions
from session import createSession from session import createSession
from session import getJson from session import getJson
from webfinger import webfingerHandle from webfinger import webfingerHandle
from pprint import pprint from pprint import pprint
from cache import getPersonFromCache
def generateRSAKey() -> (str, str): def generateRSAKey() -> (str, str):
@ -93,8 +96,7 @@ def setProfileImage(baseDir: str, httpPrefix: str, nickname: str, domain: str,
if imageFilename.startswith('~/'): if imageFilename.startswith('~/'):
imageFilename = imageFilename.replace('~/', str(Path.home()) + '/') imageFilename = imageFilename.replace('~/', str(Path.home()) + '/')
if ':' in domain: domain = removeDomainPort(domain)
domain = domain.split(':')[0]
fullDomain = getFullDomain(domain, port) fullDomain = getFullDomain(domain, port)
handle = nickname + '@' + domain handle = nickname + '@' + domain
@ -147,8 +149,7 @@ def setProfileImage(baseDir: str, httpPrefix: str, nickname: str, domain: str,
def _accountExists(baseDir: str, nickname: str, domain: str) -> bool: def _accountExists(baseDir: str, nickname: str, domain: str) -> bool:
"""Returns true if the given account exists """Returns true if the given account exists
""" """
if ':' in domain: domain = removeDomainPort(domain)
domain = domain.split(':')[0]
return os.path.isdir(baseDir + '/accounts/' + nickname + '@' + domain) or \ return os.path.isdir(baseDir + '/accounts/' + nickname + '@' + domain) or \
os.path.isdir(baseDir + '/deactivated/' + nickname + '@' + domain) os.path.isdir(baseDir + '/deactivated/' + nickname + '@' + domain)
@ -720,8 +721,7 @@ def personLookup(domain: str, path: str, baseDir: str) -> {}:
return None return None
if not isSharedInbox and not validNickname(domain, nickname): if not isSharedInbox and not validNickname(domain, nickname):
return None return None
if ':' in domain: domain = removeDomainPort(domain)
domain = domain.split(':')[0]
handle = nickname + '@' + domain handle = nickname + '@' + domain
filename = baseDir + '/accounts/' + handle + '.json' filename = baseDir + '/accounts/' + handle + '.json'
if not os.path.isfile(filename): if not os.path.isfile(filename):
@ -1349,3 +1349,31 @@ def getActorJson(hostDomain: str, handle: str, http: bool, gnunet: bool,
pprint(personJson) pprint(personJson)
return personJson, asHeader return personJson, asHeader
return None, None return None, None
def getPersonAvatarUrl(baseDir: str, personUrl: str, personCache: {},
allowDownloads: bool) -> str:
"""Returns the avatar url for the person
"""
personJson = \
getPersonFromCache(baseDir, personUrl, personCache, allowDownloads)
if not personJson:
return None
# get from locally stored image
if not personJson.get('id'):
return None
actorStr = personJson['id'].replace('/', '-')
avatarImagePath = baseDir + '/cache/avatars/' + actorStr
imageExtension = getImageExtensions()
for ext in imageExtension:
if os.path.isfile(avatarImagePath + '.' + ext):
return '/avatars/' + actorStr + '.' + ext
elif os.path.isfile(avatarImagePath.lower() + '.' + ext):
return '/avatars/' + actorStr.lower() + '.' + ext
if personJson.get('icon'):
if personJson['icon'].get('url'):
return personJson['icon']['url']
return None

View File

@ -32,6 +32,8 @@ from session import postImage
from webfinger import webfingerHandle from webfinger import webfingerHandle
from httpsig import createSignedHeader from httpsig import createSignedHeader
from siteactive import siteIsActive from siteactive import siteIsActive
from domainhandler import removeDomainPort
from domainhandler import getPortFromDomain
from utils import hasObjectDict from utils import hasObjectDict
from utils import rejectPostId from utils import rejectPostId
from utils import removeInvalidChars from utils import removeInvalidChars
@ -683,8 +685,7 @@ def savePostToBox(baseDir: str, httpPrefix: str, postId: str,
boxname != 'scheduled': boxname != 'scheduled':
return None return None
originalDomain = domain originalDomain = domain
if ':' in domain: domain = removeDomainPort(domain)
domain = domain.split(':')[0]
if not postId: if not postId:
statusNumber, published = getStatusNumber() statusNumber, published = getStatusNumber()
@ -1077,6 +1078,7 @@ def _createPostBase(baseDir: str, nickname: str, domain: str, port: int,
'atomUri': newPostId, 'atomUri': newPostId,
'inReplyToAtomUri': inReplyToAtomUri, 'inReplyToAtomUri': inReplyToAtomUri,
'commentsEnabled': commentsEnabled, 'commentsEnabled': commentsEnabled,
'rejectReplies': not commentsEnabled,
'mediaType': 'text/html', 'mediaType': 'text/html',
'content': content, 'content': content,
'contentMap': { 'contentMap': {
@ -1128,6 +1130,7 @@ def _createPostBase(baseDir: str, nickname: str, domain: str, port: int,
'atomUri': newPostId, 'atomUri': newPostId,
'inReplyToAtomUri': inReplyToAtomUri, 'inReplyToAtomUri': inReplyToAtomUri,
'commentsEnabled': commentsEnabled, 'commentsEnabled': commentsEnabled,
'rejectReplies': not commentsEnabled,
'mediaType': 'text/html', 'mediaType': 'text/html',
'content': content, 'content': content,
'contentMap': { 'contentMap': {
@ -1689,7 +1692,7 @@ def getMentionedPeople(baseDir: str, httpPrefix: str,
mentionedNickname = handle.split('@')[0] mentionedNickname = handle.split('@')[0]
mentionedDomain = handle.split('@')[1].strip('\n').strip('\r') mentionedDomain = handle.split('@')[1].strip('\n').strip('\r')
if ':' in mentionedDomain: if ':' in mentionedDomain:
mentionedDomain = mentionedDomain.split(':')[0] mentionedDomain = removeDomainPort(mentionedDomain)
if not validNickname(mentionedDomain, mentionedNickname): if not validNickname(mentionedDomain, mentionedNickname):
continue continue
actor = \ actor = \
@ -2665,8 +2668,8 @@ def sendToFollowers(session, baseDir: str,
index = 0 index = 0
toDomain = followerHandles[index].split('@')[1] toDomain = followerHandles[index].split('@')[1]
if ':' in toDomain: if ':' in toDomain:
toPort = toDomain.split(':')[1] toPort = getPortFromDomain(toDomain)
toDomain = toDomain.split(':')[0] toDomain = removeDomainPort(toDomain)
cc = '' cc = ''

View File

@ -15,6 +15,7 @@ JSON-LD.
__copyright__ = 'Copyright (c) 2011-2014 Digital Bazaar, Inc.' __copyright__ = 'Copyright (c) 2011-2014 Digital Bazaar, Inc.'
__license__ = 'New BSD license' __license__ = 'New BSD license'
__version__ = '0.6.8' __version__ = '0.6.8'
__module_group__ = "ActivityPub"
__all__ = [ __all__ = [
'compact', 'expand', 'flatten', 'frame', 'link', 'from_rdf', 'to_rdf', 'compact', 'expand', 'flatten', 'frame', 'link', 'from_rdf', 'to_rdf',

View File

@ -10,6 +10,7 @@ import os
from utils import loadJson from utils import loadJson
from utils import saveJson from utils import saveJson
from utils import getStatusNumber from utils import getStatusNumber
from domainhandler import removeDomainPort
def _clearRoleStatus(baseDir: str, role: str) -> None: def _clearRoleStatus(baseDir: str, role: str) -> None:
@ -75,8 +76,7 @@ def _addRole(baseDir: str, nickname: str, domain: str,
"""Adds a role nickname to the file. """Adds a role nickname to the file.
This is a file containing the nicknames of accounts having this role This is a file containing the nicknames of accounts having this role
""" """
if ':' in domain: domain = removeDomainPort(domain)
domain = domain.split(':')[0]
roleFile = baseDir + '/accounts/' + roleFilename roleFile = baseDir + '/accounts/' + roleFilename
if os.path.isfile(roleFile): if os.path.isfile(roleFile):
# is this nickname already in the file? # is this nickname already in the file?

View File

@ -20,6 +20,7 @@ from utils import loadJson
from utils import saveJson from utils import saveJson
from utils import getImageExtensions from utils import getImageExtensions
from utils import hasObjectDict from utils import hasObjectDict
from domainhandler import removeDomainPort
from media import processMetaData from media import processMetaData
@ -187,9 +188,7 @@ def expireShares(baseDir: str) -> None:
def _expireSharesForAccount(baseDir: str, nickname: str, domain: str) -> None: def _expireSharesForAccount(baseDir: str, nickname: str, domain: str) -> None:
"""Removes expired items from shares for a particular account """Removes expired items from shares for a particular account
""" """
handleDomain = domain handleDomain = removeDomainPort(domain)
if ':' in handleDomain:
handleDomain = domain.split(':')[0]
handle = nickname + '@' + handleDomain handle = nickname + '@' + handleDomain
sharesFilename = baseDir + '/accounts/' + handle + '/shares.json' sharesFilename = baseDir + '/accounts/' + handle + '/shares.json'
if os.path.isfile(sharesFilename): if os.path.isfile(sharesFilename):
@ -250,9 +249,7 @@ def getSharesFeedForPerson(baseDir: str,
domain = getFullDomain(domain, port) domain = getFullDomain(domain, port)
handleDomain = domain handleDomain = removeDomainPort(domain)
if ':' in handleDomain:
handleDomain = domain.split(':')[0]
handle = nickname + '@' + handleDomain handle = nickname + '@' + handleDomain
sharesFilename = baseDir + '/accounts/' + handle + '/shares.json' sharesFilename = baseDir + '/accounts/' + handle + '/shares.json'

View File

@ -5,6 +5,7 @@ __version__ = "1.2.0"
__maintainer__ = "Bob Mottram" __maintainer__ = "Bob Mottram"
__email__ = "bob@freedombone.net" __email__ = "bob@freedombone.net"
__status__ = "Production" __status__ = "Production"
__module_group__ = "Testing"
import time import time
import os import os
@ -115,8 +116,8 @@ from newswire import parseFeedDate
from mastoapiv1 import getMastoApiV1IdFromNickname from mastoapiv1 import getMastoApiV1IdFromNickname
from mastoapiv1 import getNicknameFromMastoApiV1Id from mastoapiv1 import getNicknameFromMastoApiV1Id
from webapp_post import prepareHtmlPostNickname from webapp_post import prepareHtmlPostNickname
from webapp_utils import markdownToHtml
from speaker import speakerReplaceLinks from speaker import speakerReplaceLinks
from markdown import markdownToHtml
testServerAliceRunning = False testServerAliceRunning = False
testServerBobRunning = False testServerBobRunning = False
@ -2359,6 +2360,16 @@ def _testJsonPostAllowsComments():
"commentsEnabled": False "commentsEnabled": False
} }
assert not jsonPostAllowsComments(postJsonObject) assert not jsonPostAllowsComments(postJsonObject)
postJsonObject = {
"id": "123",
"rejectReplies": False
}
assert jsonPostAllowsComments(postJsonObject)
postJsonObject = {
"id": "123",
"rejectReplies": True
}
assert not jsonPostAllowsComments(postJsonObject)
postJsonObject = { postJsonObject = {
"id": "123", "id": "123",
"commentsEnabled": True "commentsEnabled": True

View File

@ -7,6 +7,7 @@ __email__ = "bob@freedombone.net"
__status__ = "Production" __status__ = "Production"
import os import os
from utils import isAccountDir
from utils import loadJson from utils import loadJson
from utils import saveJson from utils import saveJson
from utils import getImageExtensions from utils import getImageExtensions
@ -623,11 +624,7 @@ def _setThemeImages(baseDir: str, name: str) -> None:
for subdir, dirs, files in os.walk(baseDir + '/accounts'): for subdir, dirs, files in os.walk(baseDir + '/accounts'):
for acct in dirs: for acct in dirs:
if '@' not in acct: if not isAccountDir(acct):
continue
if acct.startswith('inbox@'):
continue
elif acct.startswith('news@'):
continue continue
accountDir = \ accountDir = \
os.path.join(baseDir + '/accounts', acct) os.path.join(baseDir + '/accounts', acct)

184
utils.py
View File

@ -5,6 +5,7 @@ __version__ = "1.2.0"
__maintainer__ = "Bob Mottram" __maintainer__ = "Bob Mottram"
__email__ = "bob@freedombone.net" __email__ = "bob@freedombone.net"
__status__ = "Production" __status__ = "Production"
__module_group__ = "ActivityPu"
import os import os
import re import re
@ -15,6 +16,8 @@ import json
import idna import idna
import locale import locale
from pprint import pprint from pprint import pprint
from domainhandler import removeDomainPort
from domainhandler import getPortFromDomain
from followingCalendar import addPersonToCalendar from followingCalendar import addPersonToCalendar
from cryptography.hazmat.backends import default_backend from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives import hashes
@ -417,8 +420,7 @@ def getFollowersOfPerson(baseDir: str,
Used by the shared inbox to know who to send incoming mail to Used by the shared inbox to know who to send incoming mail to
""" """
followers = [] followers = []
if ':' in domain: domain = removeDomainPort(domain)
domain = domain.split(':')[0]
handle = nickname + '@' + domain handle = nickname + '@' + domain
if not os.path.isdir(baseDir + '/accounts/' + handle): if not os.path.isdir(baseDir + '/accounts/' + handle):
return followers return followers
@ -645,8 +647,7 @@ def createInboxQueueDir(nickname: str, domain: str, baseDir: str) -> str:
def domainPermitted(domain: str, federationList: []): def domainPermitted(domain: str, federationList: []):
if len(federationList) == 0: if len(federationList) == 0:
return True return True
if ':' in domain: domain = removeDomainPort(domain)
domain = domain.split(':')[0]
if domain in federationList: if domain in federationList:
return True return True
return False return False
@ -742,50 +743,59 @@ def getDisplayName(baseDir: str, actor: str, personCache: {}) -> str:
return nameFound return nameFound
def _genderFromString(translate: {}, text: str) -> str:
"""Given some text, does it contain a gender description?
"""
gender = None
textOrig = text
text = text.lower()
if translate['He/Him'].lower() in text or \
translate['boy'].lower() in text:
gender = 'He/Him'
elif (translate['She/Her'].lower() in text or
translate['girl'].lower() in text):
gender = 'She/Her'
elif 'him' in text or 'male' in text:
gender = 'He/Him'
elif 'her' in text or 'she' in text or \
'fem' in text or 'woman' in text:
gender = 'She/Her'
elif 'man' in text or 'He' in textOrig:
gender = 'He/Him'
return gender
def getGenderFromBio(baseDir: str, actor: str, personCache: {}, def getGenderFromBio(baseDir: str, actor: str, personCache: {},
translate: {}) -> str: translate: {}) -> str:
"""Tries to ascertain gender from bio description """Tries to ascertain gender from bio description
This is for use by text-to-speech for pitch setting
""" """
defaultGender = 'They/Them'
if '/statuses/' in actor: if '/statuses/' in actor:
actor = actor.split('/statuses/')[0] actor = actor.split('/statuses/')[0]
if not personCache.get(actor): if not personCache.get(actor):
return None return defaultGender
bioFound = None bioFound = None
if translate: if translate:
pronounStr = translate['pronoun'].lower() pronounStr = translate['pronoun'].lower()
else: else:
pronounStr = 'pronoun' pronounStr = 'pronoun'
actorJson = None
if personCache[actor].get('actor'): if personCache[actor].get('actor'):
# is gender defined as a profile tag? actorJson = personCache[actor]['actor']
if personCache[actor]['actor'].get('attachment'):
tagsList = personCache[actor]['actor']['attachment']
if isinstance(tagsList, list):
for tag in tagsList:
if not isinstance(tag, dict):
continue
if not tag.get('name') or not tag.get('value'):
continue
if tag['name'].lower() == \
translate['gender'].lower():
bioFound = tag['value']
break
elif tag['name'].lower().startswith(pronounStr):
bioFound = tag['value']
break
# if not then use the bio
if not bioFound and personCache[actor]['actor'].get('summary'):
bioFound = personCache[actor]['actor']['summary']
else: else:
# Try to obtain from the cached actors # Try to obtain from the cached actors
cachedActorFilename = \ cachedActorFilename = \
baseDir + '/cache/actors/' + (actor.replace('/', '#')) + '.json' baseDir + '/cache/actors/' + (actor.replace('/', '#')) + '.json'
if os.path.isfile(cachedActorFilename): if os.path.isfile(cachedActorFilename):
actorJson = loadJson(cachedActorFilename, 1) actorJson = loadJson(cachedActorFilename, 1)
if actorJson: if not actorJson:
return defaultGender
# is gender defined as a profile tag? # is gender defined as a profile tag?
if actorJson.get('attachment'): if actorJson.get('attachment'):
tagsList = actorJson['attachment'] tagsList = actorJson['attachment']
if isinstance(tagsList, list): if isinstance(tagsList, list):
# look for a gender field name
for tag in tagsList: for tag in tagsList:
if not isinstance(tag, dict): if not isinstance(tag, dict):
continue continue
@ -798,27 +808,25 @@ def getGenderFromBio(baseDir: str, actor: str, personCache: {},
elif tag['name'].lower().startswith(pronounStr): elif tag['name'].lower().startswith(pronounStr):
bioFound = tag['value'] bioFound = tag['value']
break break
# the field name could be anything,
# just look at the value
if not bioFound:
for tag in tagsList:
if not isinstance(tag, dict):
continue
if not tag.get('name') or not tag.get('value'):
continue
gender = _genderFromString(translate, tag['value'])
if gender:
return gender
# if not then use the bio # if not then use the bio
if not bioFound and actorJson.get('summary'): if not bioFound and actorJson.get('summary'):
bioFound = actorJson['summary'] bioFound = actorJson['summary']
if not bioFound: if not bioFound:
return None return defaultGender
gender = 'They/Them' gender = _genderFromString(translate, bioFound)
bioFoundOrig = bioFound if not gender:
bioFound = bioFound.lower() gender = defaultGender
if translate['He/Him'].lower() in bioFound or \
translate['boy'].lower() in bioFound:
gender = 'He/Him'
elif (translate['She/Her'].lower() in bioFound or
translate['girl'].lower() in bioFound):
gender = 'She/Her'
elif 'him' in bioFound or 'male' in bioFound:
gender = 'He/Him'
elif 'her' in bioFound or 'she' in bioFound or \
'fem' in bioFound or 'woman' in bioFound:
gender = 'She/Her'
elif 'man' in bioFound or 'He' in bioFoundOrig:
gender = 'He/Him'
return gender return gender
@ -827,32 +835,15 @@ def getNicknameFromActor(actor: str) -> str:
""" """
if actor.startswith('@'): if actor.startswith('@'):
actor = actor[1:] actor = actor[1:]
if '/users/' not in actor: usersPaths = ('/users/', '/profile/', '/channel/', '/accounts/', '/u/')
if '/profile/' in actor: for possiblePath in usersPaths:
nickStr = actor.split('/profile/')[1].replace('@', '') if possiblePath in actor:
nickStr = actor.split(possiblePath)[1].replace('@', '')
if '/' not in nickStr: if '/' not in nickStr:
return nickStr return nickStr
else: else:
return nickStr.split('/')[0] return nickStr.split('/')[0]
elif '/channel/' in actor: if '/@' in actor:
nickStr = actor.split('/channel/')[1].replace('@', '')
if '/' not in nickStr:
return nickStr
else:
return nickStr.split('/')[0]
elif '/accounts/' in actor:
nickStr = actor.split('/accounts/')[1].replace('@', '')
if '/' not in nickStr:
return nickStr
else:
return nickStr.split('/')[0]
elif '/u/' in actor:
nickStr = actor.split('/u/')[1].replace('@', '')
if '/' not in nickStr:
return nickStr
else:
return nickStr.split('/')[0]
elif '/@' in actor:
# https://domain/@nick # https://domain/@nick
nickStr = actor.split('/@')[1] nickStr = actor.split('/@')[1]
if '/' in nickStr: if '/' in nickStr:
@ -872,11 +863,6 @@ def getNicknameFromActor(actor: str) -> str:
return None return None
return nickStr return nickStr
return None return None
nickStr = actor.split('/users/')[1].replace('@', '')
if '/' not in nickStr:
return nickStr
else:
return nickStr.split('/')[0]
def getDomainFromActor(actor: str) -> (str, int): def getDomainFromActor(actor: str) -> (str, int):
@ -886,27 +872,14 @@ def getDomainFromActor(actor: str) -> (str, int):
actor = actor[1:] actor = actor[1:]
port = None port = None
prefixes = getProtocolPrefixes() prefixes = getProtocolPrefixes()
if '/profile/' in actor: usersPaths = ('/users/', '/profile/', '/accounts/', '/channel/', '/u/')
domain = actor.split('/profile/')[0] for possiblePath in usersPaths:
if possiblePath in actor:
domain = actor.split(possiblePath)[0]
for prefix in prefixes: for prefix in prefixes:
domain = domain.replace(prefix, '') domain = domain.replace(prefix, '')
elif '/accounts/' in actor: break
domain = actor.split('/accounts/')[0] if '/@' in actor:
for prefix in prefixes:
domain = domain.replace(prefix, '')
elif '/channel/' in actor:
domain = actor.split('/channel/')[0]
for prefix in prefixes:
domain = domain.replace(prefix, '')
elif '/users/' in actor:
domain = actor.split('/users/')[0]
for prefix in prefixes:
domain = domain.replace(prefix, '')
elif '/u/' in actor:
domain = actor.split('/u/')[0]
for prefix in prefixes:
domain = domain.replace(prefix, '')
elif '/@' in actor:
domain = actor.split('/@')[0] domain = actor.split('/@')[0]
for prefix in prefixes: for prefix in prefixes:
domain = domain.replace(prefix, '') domain = domain.replace(prefix, '')
@ -919,11 +892,8 @@ def getDomainFromActor(actor: str) -> (str, int):
if '/' in actor: if '/' in actor:
domain = domain.split('/')[0] domain = domain.split('/')[0]
if ':' in domain: if ':' in domain:
portStr = domain.split(':')[1] port = getPortFromDomain(domain)
if not portStr.isdigit(): domain = removeDomainPort(domain)
return None, None
port = int(portStr)
domain = domain.split(':')[0]
return domain, port return domain, port
@ -932,8 +902,7 @@ def _setDefaultPetName(baseDir: str, nickname: str, domain: str,
"""Sets a default petname """Sets a default petname
This helps especially when using onion or i2p address This helps especially when using onion or i2p address
""" """
if ':' in domain: domain = removeDomainPort(domain)
domain = domain.split(':')[0]
userPath = baseDir + '/accounts/' + nickname + '@' + domain userPath = baseDir + '/accounts/' + nickname + '@' + domain
petnamesFilename = userPath + '/petnames.txt' petnamesFilename = userPath + '/petnames.txt'
@ -975,7 +944,8 @@ def followPerson(baseDir: str, nickname: str, domain: str,
print('DEBUG: follow of domain ' + followDomain) print('DEBUG: follow of domain ' + followDomain)
if ':' in domain: if ':' in domain:
handle = nickname + '@' + domain.split(':')[0] domainOnly = removeDomainPort(domain)
handle = nickname + '@' + domainOnly
else: else:
handle = nickname + '@' + domain handle = nickname + '@' + domain
@ -984,7 +954,8 @@ def followPerson(baseDir: str, nickname: str, domain: str,
return False return False
if ':' in followDomain: if ':' in followDomain:
handleToFollow = followNickname + '@' + followDomain.split(':')[0] followDomainOnly = removeDomainPort(followDomain)
handleToFollow = followNickname + '@' + followDomainOnly
else: else:
handleToFollow = followNickname + '@' + followDomain handleToFollow = followNickname + '@' + followDomain
@ -1189,10 +1160,6 @@ def _removeAttachment(baseDir: str, httpPrefix: str, domain: str,
return return
if not postJson['attachment'][0].get('url'): if not postJson['attachment'][0].get('url'):
return return
# if port:
# if port != 80 and port != 443:
# if ':' not in domain:
# domain = domain + ':' + str(port)
attachmentUrl = postJson['attachment'][0]['url'] attachmentUrl = postJson['attachment'][0]['url']
if not attachmentUrl: if not attachmentUrl:
return return
@ -1487,12 +1454,12 @@ def noOfActiveAccountsMonthly(baseDir: str, months: int) -> bool:
monthSeconds = int(60*60*24*30*months) monthSeconds = int(60*60*24*30*months)
for subdir, dirs, files in os.walk(baseDir + '/accounts'): for subdir, dirs, files in os.walk(baseDir + '/accounts'):
for account in dirs: for account in dirs:
if '@' in account: if not isAccountDir(account):
if not account.startswith('inbox@') and \ continue
not account.startswith('news@'):
lastUsedFilename = \ lastUsedFilename = \
baseDir + '/accounts/' + account + '/.lastUsed' baseDir + '/accounts/' + account + '/.lastUsed'
if os.path.isfile(lastUsedFilename): if not os.path.isfile(lastUsedFilename):
continue
with open(lastUsedFilename, 'r') as lastUsedFile: with open(lastUsedFilename, 'r') as lastUsedFile:
lastUsed = lastUsedFile.read() lastUsed = lastUsedFile.read()
if lastUsed.isdigit(): if lastUsed.isdigit():
@ -1824,13 +1791,6 @@ def getFileCaseInsensitive(path: str) -> str:
if path != path.lower(): if path != path.lower():
if os.path.isfile(path.lower()): if os.path.isfile(path.lower()):
return path.lower() return path.lower()
# directory, filename = os.path.split(path)
# directory, filename = (directory or '.'), filename.lower()
# for f in os.listdir(directory):
# if f.lower() == filename:
# newpath = os.path.join(directory, f)
# if os.path.isfile(newpath):
# return newpath
return None return None

View File

@ -12,7 +12,7 @@ from shutil import copyfile
from utils import getConfigParam from utils import getConfigParam
from webapp_utils import htmlHeaderWithWebsiteMarkup from webapp_utils import htmlHeaderWithWebsiteMarkup
from webapp_utils import htmlFooter from webapp_utils import htmlFooter
from webapp_utils import markdownToHtml from markdown import markdownToHtml
def htmlAbout(cssCache: {}, baseDir: str, httpPrefix: str, def htmlAbout(cssCache: {}, baseDir: str, httpPrefix: str,

View File

@ -18,6 +18,7 @@ from utils import getDomainFromActor
from utils import locatePost from utils import locatePost
from utils import loadJson from utils import loadJson
from utils import weekDayOfMonthStart from utils import weekDayOfMonthStart
from domainhandler import removeDomainPort
from happening import getTodaysEvents from happening import getTodaysEvents
from happening import getCalendarEvents from happening import getCalendarEvents
from webapp_utils import htmlHeaderWithExternalStyle from webapp_utils import htmlHeaderWithExternalStyle
@ -247,9 +248,7 @@ def htmlCalendar(personCache: {}, cssCache: {}, translate: {},
textModeBanner: str, accessKeys: {}) -> str: textModeBanner: str, accessKeys: {}) -> str:
"""Show the calendar for a person """Show the calendar for a person
""" """
domain = domainFull domain = removeDomainPort(domainFull)
if ':' in domainFull:
domain = domainFull.split(':')[0]
monthNumber = 0 monthNumber = 0
dayNumber = None dayNumber = None
@ -460,7 +459,6 @@ def htmlCalendar(personCache: {}, cssCache: {}, translate: {},
htmlHideFromScreenReader('') + ' ' + translate['Previous month'] htmlHideFromScreenReader('') + ' ' + translate['Previous month']
navLinks[prevMonthStr] = calActor + '/calendar?year=' + str(prevYear) + \ navLinks[prevMonthStr] = calActor + '/calendar?year=' + str(prevYear) + \
'?month=' + str(prevMonthNumber) '?month=' + str(prevMonthNumber)
# TODO
navAccessKeys = { navAccessKeys = {
} }
screenReaderCal = \ screenReaderCal = \

View File

@ -11,6 +11,7 @@ import os
from utils import getConfigParam from utils import getConfigParam
from utils import getNicknameFromActor from utils import getNicknameFromActor
from utils import isEditor from utils import isEditor
from domainhandler import removeDomainPort
from webapp_utils import sharesTimelineJson from webapp_utils import sharesTimelineJson
from webapp_utils import htmlPostSeparator from webapp_utils import htmlPostSeparator
from webapp_utils import getLeftImageFile from webapp_utils import getLeftImageFile
@ -76,9 +77,7 @@ def getLeftColumnContent(baseDir: str, nickname: str, domainFull: str,
htmlStr = '' htmlStr = ''
separatorStr = htmlPostSeparator(baseDir, 'left') separatorStr = htmlPostSeparator(baseDir, 'left')
domain = domainFull domain = removeDomainPort(domainFull)
if ':' in domain:
domain = domain.split(':')
editImageClass = '' editImageClass = ''
if showHeaderImage: if showHeaderImage:
@ -298,9 +297,7 @@ def htmlLinksMobile(cssCache: {}, baseDir: str,
else: else:
editor = isEditor(baseDir, nickname) editor = isEditor(baseDir, nickname)
domain = domainFull domain = removeDomainPort(domainFull)
if ':' in domain:
domain = domain.split(':')[0]
instanceTitle = \ instanceTitle = \
getConfigParam(baseDir, 'instanceTitle') getConfigParam(baseDir, 'instanceTitle')

View File

@ -17,6 +17,7 @@ from utils import votesOnNewswireItem
from utils import getNicknameFromActor from utils import getNicknameFromActor
from utils import isEditor from utils import isEditor
from utils import getConfigParam from utils import getConfigParam
from domainhandler import removeDomainPort
from posts import isModerator from posts import isModerator
from webapp_utils import getRightImageFile from webapp_utils import getRightImageFile
from webapp_utils import htmlHeaderWithExternalStyle from webapp_utils import htmlHeaderWithExternalStyle
@ -58,9 +59,7 @@ def getRightColumnContent(baseDir: str, nickname: str, domainFull: str,
""" """
htmlStr = '' htmlStr = ''
domain = domainFull domain = removeDomainPort(domainFull)
if ':' in domain:
domain = domain.split(':')
if authorized: if authorized:
# only show the publish button if logged in, otherwise replace it with # only show the publish button if logged in, otherwise replace it with

View File

@ -0,0 +1,40 @@
__filename__ = "webapp_minimalbutton.py"
__author__ = "Bob Mottram"
__license__ = "AGPL3+"
__version__ = "1.2.0"
__maintainer__ = "Bob Mottram"
__email__ = "bob@freedombone.net"
__status__ = "Production"
__module_group__ = "Web Interface"
import os
def isMinimal(baseDir: str, domain: str, nickname: str) -> bool:
"""Returns true if minimal buttons should be shown
for the given account
"""
accountDir = baseDir + '/accounts/' + \
nickname + '@' + domain
if not os.path.isdir(accountDir):
return True
minimalFilename = accountDir + '/.notminimal'
if os.path.isfile(minimalFilename):
return False
return True
def setMinimal(baseDir: str, domain: str, nickname: str,
minimal: bool) -> None:
"""Sets whether an account should display minimal buttons
"""
accountDir = baseDir + '/accounts/' + nickname + '@' + domain
if not os.path.isdir(accountDir):
return
minimalFilename = accountDir + '/.notminimal'
minimalFileExists = os.path.isfile(minimalFilename)
if minimal and minimalFileExists:
os.remove(minimalFilename)
elif not minimal and not minimalFileExists:
with open(minimalFilename, 'w+') as fp:
fp.write('\n')

View File

@ -8,6 +8,7 @@ __status__ = "Production"
__module_group__ = "Web Interface" __module_group__ = "Web Interface"
import os import os
from utils import isAccountDir
from utils import getFullDomain from utils import getFullDomain
from utils import isEditor from utils import isEditor
from utils import loadJson from utils import loadJson
@ -270,11 +271,7 @@ def htmlModerationInfo(cssCache: {}, translate: {},
accounts = [] accounts = []
for subdir, dirs, files in os.walk(baseDir + '/accounts'): for subdir, dirs, files in os.walk(baseDir + '/accounts'):
for acct in dirs: for acct in dirs:
if '@' not in acct: if not isAccountDir(acct):
continue
if acct.startswith('inbox@'):
continue
elif acct.startswith('news@'):
continue continue
accounts.append(acct) accounts.append(acct)
break break

View File

@ -52,9 +52,9 @@ from content import removeLongWords
from content import getMentionsFromHtml from content import getMentionsFromHtml
from content import switchWords from content import switchWords
from person import isPersonSnoozed from person import isPersonSnoozed
from person import getPersonAvatarUrl
from announce import announcedByPerson from announce import announcedByPerson
from webapp_utils import getAvatarImageUrl from webapp_utils import getAvatarImageUrl
from webapp_utils import getPersonAvatarUrl
from webapp_utils import updateAvatarImageCache from webapp_utils import updateAvatarImageCache
from webapp_utils import loadIndividualPostAsHtmlFromCache from webapp_utils import loadIndividualPostAsHtmlFromCache
from webapp_utils import addEmojiToDisplayName from webapp_utils import addEmojiToDisplayName
@ -1401,6 +1401,9 @@ def individualPostAsHtml(allowDownloads: bool,
if 'commentsEnabled' in postJsonObject['object']: if 'commentsEnabled' in postJsonObject['object']:
if postJsonObject['object']['commentsEnabled'] is False: if postJsonObject['object']['commentsEnabled'] is False:
commentsEnabled = False commentsEnabled = False
elif 'rejectReplies' in postJsonObject['object']:
if postJsonObject['object']['rejectReplies']:
commentsEnabled = False
replyStr = _getReplyIconHtml(nickname, isPublicRepeat, replyStr = _getReplyIconHtml(nickname, isPublicRepeat,
showIcons, commentsEnabled, showIcons, commentsEnabled,

View File

@ -26,6 +26,7 @@ from skills import getSkills
from theme import getThemesList from theme import getThemesList
from person import personBoxJson from person import personBoxJson
from person import getActorJson from person import getActorJson
from person import getPersonAvatarUrl
from webfinger import webfingerHandle from webfinger import webfingerHandle
from posts import parseUserFeed from posts import parseUserFeed
from posts import getPersonBox from posts import getPersonBox
@ -45,7 +46,6 @@ from webapp_frontscreen import htmlFrontScreen
from webapp_utils import htmlKeyboardNavigation from webapp_utils import htmlKeyboardNavigation
from webapp_utils import htmlHideFromScreenReader from webapp_utils import htmlHideFromScreenReader
from webapp_utils import scheduledPostsExist from webapp_utils import scheduledPostsExist
from webapp_utils import getPersonAvatarUrl
from webapp_utils import htmlHeaderWithExternalStyle from webapp_utils import htmlHeaderWithExternalStyle
from webapp_utils import htmlHeaderWithPersonMarkup from webapp_utils import htmlHeaderWithPersonMarkup
from webapp_utils import htmlFooter from webapp_utils import htmlFooter

View File

@ -11,6 +11,7 @@ import os
from shutil import copyfile from shutil import copyfile
import urllib.parse import urllib.parse
from datetime import datetime from datetime import datetime
from utils import isAccountDir
from utils import getConfigParam from utils import getConfigParam
from utils import getFullDomain from utils import getFullDomain
from utils import isEditor from utils import isEditor
@ -407,11 +408,7 @@ def htmlSkillsSearch(actor: str,
for f in files: for f in files:
if not f.endswith('.json'): if not f.endswith('.json'):
continue continue
if '@' not in f: if not isAccountDir(f):
continue
if f.startswith('inbox@'):
continue
elif f.startswith('news@'):
continue continue
actorFilename = os.path.join(subdir, f) actorFilename = os.path.join(subdir, f)
actorJson = loadJson(actorFilename) actorJson = loadJson(actorFilename)
@ -446,11 +443,7 @@ def htmlSkillsSearch(actor: str,
for f in files: for f in files:
if not f.endswith('.json'): if not f.endswith('.json'):
continue continue
if '@' not in f: if not isAccountDir(f):
continue
if f.startswith('inbox@'):
continue
elif f.startswith('news@'):
continue continue
actorFilename = os.path.join(subdir, f) actorFilename = os.path.join(subdir, f)
cachedActorJson = loadJson(actorFilename) cachedActorJson = loadJson(actorFilename)

View File

@ -17,7 +17,7 @@ from utils import isEditor
from utils import removeIdEnding from utils import removeIdEnding
from follow import followerApprovalActive from follow import followerApprovalActive
from person import isPersonSnoozed from person import isPersonSnoozed
from webapp_utils import markdownToHtml from markdown import markdownToHtml
from webapp_utils import htmlKeyboardNavigation from webapp_utils import htmlKeyboardNavigation
from webapp_utils import htmlHideFromScreenReader from webapp_utils import htmlHideFromScreenReader
from webapp_utils import htmlPostSeparator from webapp_utils import htmlPostSeparator

View File

@ -12,7 +12,7 @@ from shutil import copyfile
from utils import getConfigParam from utils import getConfigParam
from webapp_utils import htmlHeaderWithExternalStyle from webapp_utils import htmlHeaderWithExternalStyle
from webapp_utils import htmlFooter from webapp_utils import htmlFooter
from webapp_utils import markdownToHtml from markdown import markdownToHtml
def htmlTermsOfService(cssCache: {}, baseDir: str, def htmlTermsOfService(cssCache: {}, baseDir: str,

View File

@ -16,170 +16,10 @@ from utils import getProtocolPrefixes
from utils import loadJson from utils import loadJson
from utils import getCachedPostFilename from utils import getCachedPostFilename
from utils import getConfigParam from utils import getConfigParam
from cache import getPersonFromCache
from cache import storePersonInCache from cache import storePersonInCache
from content import addHtmlTags from content import addHtmlTags
from content import replaceEmojiFromTags from content import replaceEmojiFromTags
from person import getPersonAvatarUrl
def _markdownEmphasisHtml(markdown: str) -> str:
"""Add italics and bold html markup to the given markdown
"""
replacements = {
' **': ' <b>',
'** ': '</b> ',
'**.': '</b>.',
'**:': '</b>:',
'**;': '</b>;',
'**,': '</b>,',
'**\n': '</b>\n',
' *': ' <i>',
'* ': '</i> ',
'*.': '</i>.',
'*:': '</i>:',
'*;': '</i>;',
'*,': '</i>,',
'*\n': '</i>\n',
' _': ' <ul>',
'_ ': '</ul> ',
'_.': '</ul>.',
'_:': '</ul>:',
'_;': '</ul>;',
'_,': '</ul>,',
'_\n': '</ul>\n'
}
for md, html in replacements.items():
markdown = markdown.replace(md, html)
if markdown.startswith('**'):
markdown = markdown[2:] + '<b>'
elif markdown.startswith('*'):
markdown = markdown[1:] + '<i>'
elif markdown.startswith('_'):
markdown = markdown[1:] + '<ul>'
if markdown.endswith('**'):
markdown = markdown[:len(markdown) - 2] + '</b>'
elif markdown.endswith('*'):
markdown = markdown[:len(markdown) - 1] + '</i>'
elif markdown.endswith('_'):
markdown = markdown[:len(markdown) - 1] + '</ul>'
return markdown
def _markdownReplaceQuotes(markdown: str) -> str:
"""Replaces > quotes with html blockquote
"""
if '> ' not in markdown:
return markdown
lines = markdown.split('\n')
result = ''
prevQuoteLine = None
for line in lines:
if '> ' not in line:
result += line + '\n'
prevQuoteLine = None
continue
lineStr = line.strip()
if not lineStr.startswith('> '):
result += line + '\n'
prevQuoteLine = None
continue
lineStr = lineStr.replace('> ', '', 1).strip()
if prevQuoteLine:
newPrevLine = prevQuoteLine.replace('</i></blockquote>\n', '')
result = result.replace(prevQuoteLine, newPrevLine) + ' '
lineStr += '</i></blockquote>\n'
else:
lineStr = '<blockquote><i>' + lineStr + '</i></blockquote>\n'
result += lineStr
prevQuoteLine = lineStr
if '</blockquote>\n' in result:
result = result.replace('</blockquote>\n', '</blockquote>')
if result.endswith('\n') and \
not markdown.endswith('\n'):
result = result[:len(result) - 1]
return result
def _markdownReplaceLinks(markdown: str, images: bool = False) -> str:
"""Replaces markdown links with html
Optionally replace image links
"""
replaceLinks = {}
text = markdown
startChars = '['
if images:
startChars = '!['
while startChars in text:
if ')' not in text:
break
text = text.split(startChars, 1)[1]
markdownLink = startChars + text.split(')')[0] + ')'
if ']' not in markdownLink or \
'(' not in markdownLink:
text = text.split(')', 1)[1]
continue
if not images:
replaceLinks[markdownLink] = \
'<a href="' + \
markdownLink.split('(')[1].split(')')[0] + \
'" target="_blank" rel="nofollow noopener noreferrer">' + \
markdownLink.split(startChars)[1].split(']')[0] + \
'</a>'
else:
replaceLinks[markdownLink] = \
'<img class="markdownImage" src="' + \
markdownLink.split('(')[1].split(')')[0] + \
'" alt="' + \
markdownLink.split(startChars)[1].split(']')[0] + \
'" />'
text = text.split(')', 1)[1]
for mdLink, htmlLink in replaceLinks.items():
markdown = markdown.replace(mdLink, htmlLink)
return markdown
def markdownToHtml(markdown: str) -> str:
"""Converts markdown formatted text to html
"""
markdown = _markdownReplaceQuotes(markdown)
markdown = _markdownEmphasisHtml(markdown)
markdown = _markdownReplaceLinks(markdown, True)
markdown = _markdownReplaceLinks(markdown)
# replace headers
linesList = markdown.split('\n')
htmlStr = ''
ctr = 0
for line in linesList:
if ctr > 0:
htmlStr += '<br>'
if line.startswith('#####'):
line = line.replace('#####', '').strip()
line = '<h5>' + line + '</h5>'
ctr = -1
elif line.startswith('####'):
line = line.replace('####', '').strip()
line = '<h4>' + line + '</h4>'
ctr = -1
elif line.startswith('###'):
line = line.replace('###', '').strip()
line = '<h3>' + line + '</h3>'
ctr = -1
elif line.startswith('##'):
line = line.replace('##', '').strip()
line = '<h2>' + line + '</h2>'
ctr = -1
elif line.startswith('#'):
line = line.replace('#', '').strip()
line = '<h1>' + line + '</h1>'
ctr = -1
htmlStr += line
ctr += 1
return htmlStr
def getBrokenLinkSubstitute() -> str: def getBrokenLinkSubstitute() -> str:
@ -533,34 +373,6 @@ def updateAvatarImageCache(session, baseDir: str, httpPrefix: str,
return avatarImageFilename.replace(baseDir + '/cache', '') return avatarImageFilename.replace(baseDir + '/cache', '')
def getPersonAvatarUrl(baseDir: str, personUrl: str, personCache: {},
allowDownloads: bool) -> str:
"""Returns the avatar url for the person
"""
personJson = \
getPersonFromCache(baseDir, personUrl, personCache, allowDownloads)
if not personJson:
return None
# get from locally stored image
if not personJson.get('id'):
return None
actorStr = personJson['id'].replace('/', '-')
avatarImagePath = baseDir + '/cache/avatars/' + actorStr
imageExtension = getImageExtensions()
for ext in imageExtension:
if os.path.isfile(avatarImagePath + '.' + ext):
return '/avatars/' + actorStr + '.' + ext
elif os.path.isfile(avatarImagePath.lower() + '.' + ext):
return '/avatars/' + actorStr.lower() + '.' + ext
if personJson.get('icon'):
if personJson['icon'].get('url'):
return personJson['icon']['url']
return None
def scheduledPostsExist(baseDir: str, nickname: str, domain: str) -> bool: def scheduledPostsExist(baseDir: str, nickname: str, domain: str) -> bool:
"""Returns true if there are posts scheduled to be delivered """Returns true if there are posts scheduled to be delivered
""" """
@ -1359,33 +1171,3 @@ def htmlKeyboardNavigation(banner: str, links: {}, accessKeys: {},
str(title) + '</a></label></li>\n' str(title) + '</a></label></li>\n'
htmlStr += '</ul></div>\n' htmlStr += '</ul></div>\n'
return htmlStr return htmlStr
def isMinimal(baseDir: str, domain: str, nickname: str) -> bool:
"""Returns true if minimal buttons should be shown
for the given account
"""
accountDir = baseDir + '/accounts/' + \
nickname + '@' + domain
if not os.path.isdir(accountDir):
return True
minimalFilename = accountDir + '/.notminimal'
if os.path.isfile(minimalFilename):
return False
return True
def setMinimal(baseDir: str, domain: str, nickname: str,
minimal: bool) -> None:
"""Sets whether an account should display minimal buttons
"""
accountDir = baseDir + '/accounts/' + nickname + '@' + domain
if not os.path.isdir(accountDir):
return
minimalFilename = accountDir + '/.notminimal'
minimalFileExists = os.path.isfile(minimalFilename)
if minimal and minimalFileExists:
os.remove(minimalFilename)
elif not minimal and not minimalFileExists:
with open(minimalFilename, 'w+') as fp:
fp.write('\n')

View File

@ -13,7 +13,7 @@ from utils import getConfigParam
from utils import removeHtml from utils import removeHtml
from webapp_utils import htmlHeaderWithExternalStyle from webapp_utils import htmlHeaderWithExternalStyle
from webapp_utils import htmlFooter from webapp_utils import htmlFooter
from webapp_utils import markdownToHtml from markdown import markdownToHtml
def isWelcomeScreenComplete(baseDir: str, nickname: str, domain: str) -> bool: def isWelcomeScreenComplete(baseDir: str, nickname: str, domain: str) -> bool:

View File

@ -13,7 +13,7 @@ from utils import removeHtml
from utils import getConfigParam from utils import getConfigParam
from webapp_utils import htmlHeaderWithExternalStyle from webapp_utils import htmlHeaderWithExternalStyle
from webapp_utils import htmlFooter from webapp_utils import htmlFooter
from webapp_utils import markdownToHtml from markdown import markdownToHtml
def htmlWelcomeFinal(baseDir: str, nickname: str, domain: str, def htmlWelcomeFinal(baseDir: str, nickname: str, domain: str,

View File

@ -16,7 +16,7 @@ from utils import getImageExtensions
from utils import getImageFormats from utils import getImageFormats
from webapp_utils import htmlHeaderWithExternalStyle from webapp_utils import htmlHeaderWithExternalStyle
from webapp_utils import htmlFooter from webapp_utils import htmlFooter
from webapp_utils import markdownToHtml from markdown import markdownToHtml
def htmlWelcomeProfile(baseDir: str, nickname: str, domain: str, def htmlWelcomeProfile(baseDir: str, nickname: str, domain: str,

View File

@ -17,6 +17,7 @@ from utils import loadJson
from utils import loadJsonOnionify from utils import loadJsonOnionify
from utils import saveJson from utils import saveJson
from utils import getProtocolPrefixes from utils import getProtocolPrefixes
from domainhandler import removeDomainPort
def _parseHandle(handle: str) -> (str, str): def _parseHandle(handle: str) -> (str, str):
@ -53,13 +54,8 @@ def webfingerHandle(session, handle: str, httpPrefix: str,
nickname, domain = _parseHandle(handle) nickname, domain = _parseHandle(handle)
if not nickname: if not nickname:
return None return None
wfDomain = domain wfDomain = removeDomainPort(domain)
if ':' in wfDomain:
# wfPortStr=wfDomain.split(':')[1]
# if wfPortStr.isdigit():
# wfPort=int(wfPortStr)
# if wfPort==80 or wfPort==443:
wfDomain = wfDomain.split(':')[0]
wf = getWebfingerFromCache(nickname + '@' + wfDomain, wf = getWebfingerFromCache(nickname + '@' + wfDomain,
cachedWebfingers) cachedWebfingers)
if wf: if wf: