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

main
Bob Mottram 2021-07-01 23:22:31 +01:00
commit 29613d4732
12 changed files with 299 additions and 532 deletions

340
daemon.py
View File

@ -81,7 +81,6 @@ from posts import createBlogPost
from posts import createReportPost from posts import createReportPost
from posts import createUnlistedPost from posts import createUnlistedPost
from posts import createFollowersOnlyPost from posts import createFollowersOnlyPost
from posts import createEventPost
from posts import createDirectMessagePost from posts import createDirectMessagePost
from posts import populateRepliesJson from posts import populateRepliesJson
from posts import addToField from posts import addToField
@ -114,7 +113,6 @@ from threads import threadWithTrace
from threads import removeDormantThreads from threads import removeDormantThreads
from media import replaceYouTube from media import replaceYouTube
from media import attachMedia from media import attachMedia
from media import pathIsImage
from media import pathIsVideo from media import pathIsVideo
from media import pathIsAudio from media import pathIsAudio
from blocking import updateBlockedCache from blocking import updateBlockedCache
@ -161,7 +159,6 @@ from webapp_person_options import htmlPersonOptions
from webapp_timeline import htmlShares from webapp_timeline import htmlShares
from webapp_timeline import htmlInbox from webapp_timeline import htmlInbox
from webapp_timeline import htmlBookmarks from webapp_timeline import htmlBookmarks
from webapp_timeline import htmlEvents
from webapp_timeline import htmlInboxDMs from webapp_timeline import htmlInboxDMs
from webapp_timeline import htmlInboxReplies from webapp_timeline import htmlInboxReplies
from webapp_timeline import htmlInboxMedia from webapp_timeline import htmlInboxMedia
@ -256,6 +253,7 @@ from utils import saveJson
from utils import isSuspended from utils import isSuspended
from utils import dangerousMarkup from utils import dangerousMarkup
from utils import refreshNewswire from utils import refreshNewswire
from utils import isImageFile
from manualapprove import manualDenyFollowRequest from manualapprove import manualDenyFollowRequest
from manualapprove import manualApproveFollowRequest from manualapprove import manualApproveFollowRequest
from announce import createAnnounce from announce import createAnnounce
@ -1724,7 +1722,7 @@ class PubServer(BaseHTTPRequestHandler):
print('moderationText: ' + moderationText) print('moderationText: ' + moderationText)
nickname = moderationText nickname = moderationText
if nickname.startswith('http') or \ if nickname.startswith('http') or \
nickname.startswith('dat'): nickname.startswith('hyper'):
nickname = getNicknameFromActor(nickname) nickname = getNicknameFromActor(nickname)
if '@' in nickname: if '@' in nickname:
nickname = nickname.split('@')[0] nickname = nickname.split('@')[0]
@ -1739,7 +1737,7 @@ class PubServer(BaseHTTPRequestHandler):
if moderationButton == 'block': if moderationButton == 'block':
fullBlockDomain = None fullBlockDomain = None
if moderationText.startswith('http') or \ if moderationText.startswith('http') or \
moderationText.startswith('dat'): moderationText.startswith('hyper'):
# https://domain # https://domain
blockDomain, blockPort = \ blockDomain, blockPort = \
getDomainFromActor(moderationText) getDomainFromActor(moderationText)
@ -1757,7 +1755,7 @@ class PubServer(BaseHTTPRequestHandler):
if moderationButton == 'unblock': if moderationButton == 'unblock':
fullBlockDomain = None fullBlockDomain = None
if moderationText.startswith('http') or \ if moderationText.startswith('http') or \
moderationText.startswith('dat'): moderationText.startswith('hyper'):
# https://domain # https://domain
blockDomain, blockPort = \ blockDomain, blockPort = \
getDomainFromActor(moderationText) getDomainFromActor(moderationText)
@ -5926,7 +5924,7 @@ class PubServer(BaseHTTPRequestHandler):
GETstartTime, GETtimings: {}) -> None: GETstartTime, GETtimings: {}) -> None:
"""Returns a media file """Returns a media file
""" """
if pathIsImage(path) or \ if isImageFile(path) or \
pathIsVideo(path) or \ pathIsVideo(path) or \
pathIsAudio(path): pathIsAudio(path):
mediaStr = path.split('/media/')[1] mediaStr = path.split('/media/')[1]
@ -5956,7 +5954,7 @@ class PubServer(BaseHTTPRequestHandler):
GETstartTime, GETtimings: {}) -> None: GETstartTime, GETtimings: {}) -> None:
"""Returns an emoji image """Returns an emoji image
""" """
if pathIsImage(path): if isImageFile(path):
emojiStr = path.split('/emoji/')[1] emojiStr = path.split('/emoji/')[1]
emojiFilename = baseDir + '/emoji/' + emojiStr emojiFilename = baseDir + '/emoji/' + emojiStr
if os.path.isfile(emojiFilename): if os.path.isfile(emojiFilename):
@ -9043,138 +9041,6 @@ class PubServer(BaseHTTPRequestHandler):
self.server.GETbusy = False self.server.GETbusy = False
return True return True
def _showEventsTimeline(self, authorized: bool,
callingDomain: str, path: str,
baseDir: str, httpPrefix: str,
domain: str, domainFull: str, port: int,
onionDomain: str, i2pDomain: str,
GETstartTime, GETtimings: {},
proxyType: str, cookie: str,
debug: str) -> bool:
"""Shows the events timeline
"""
if '/users/' in path:
if authorized:
# convert /events to /tlevents
if path.endswith('/events') or \
'/events?page=' in path:
path = path.replace('/events', '/tlevents')
eventsFeed = \
personBoxJson(self.server.recentPostsCache,
self.server.session,
baseDir,
domain,
port,
path,
httpPrefix,
maxPostsInFeed, 'tlevents',
authorized,
0, self.server.positiveVoting,
self.server.votingTimeMins)
print('eventsFeed: ' + str(eventsFeed))
if eventsFeed:
if self._requestHTTP():
nickname = path.replace('/users/', '')
nickname = nickname.replace('/tlevents', '')
pageNumber = 1
if '?page=' in nickname:
pageNumber = nickname.split('?page=')[1]
nickname = nickname.split('?page=')[0]
if pageNumber.isdigit():
pageNumber = int(pageNumber)
else:
pageNumber = 1
if 'page=' not in path:
# if no page was specified then show the first
eventsFeed = \
personBoxJson(self.server.recentPostsCache,
self.server.session,
baseDir,
domain,
port,
path + '?page=1',
httpPrefix,
maxPostsInFeed,
'tlevents',
authorized,
0, self.server.positiveVoting,
self.server.votingTimeMins)
fullWidthTimelineButtonHeader = \
self.server.fullWidthTimelineButtonHeader
minimalNick = isMinimal(baseDir, domain, nickname)
accessKeys = self.server.accessKeys
if self.server.keyShortcuts.get(nickname):
accessKeys = \
self.server.keyShortcuts[nickname]
msg = \
htmlEvents(self.server.cssCache,
self.server.defaultTimeline,
self.server.recentPostsCache,
self.server.maxRecentPosts,
self.server.translate,
pageNumber, maxPostsInFeed,
self.server.session,
baseDir,
self.server.cachedWebfingers,
self.server.personCache,
nickname,
domain,
port,
eventsFeed,
self.server.allowDeletion,
httpPrefix,
self.server.projectVersion,
minimalNick,
self.server.YTReplacementDomain,
self.server.showPublishedDateOnly,
self.server.newswire,
self.server.positiveVoting,
self.server.showPublishAsIcon,
fullWidthTimelineButtonHeader,
self.server.iconsAsButtons,
self.server.rssIconAtTop,
self.server.publishButtonAtTop,
authorized,
self.server.themeName,
self.server.peertubeInstances,
self.server.allowLocalNetworkAccess,
self.server.textModeBanner,
accessKeys)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
cookie, callingDomain)
self._write(msg)
self._benchmarkGETtimings(GETstartTime, GETtimings,
'show bookmarks 2 done',
'show events')
else:
# don't need authenticated fetch here because
# there is already the authorization check
msg = json.dumps(eventsFeed,
ensure_ascii=False)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('application/json', msglen,
None, callingDomain)
self._write(msg)
self.server.GETbusy = False
return True
else:
if debug:
nickname = path.replace('/users/', '')
nickname = nickname.replace('/tlevents', '')
print('DEBUG: ' + nickname +
' was not authorized to access ' + path)
if debug:
print('DEBUG: GET access to events is unauthorized')
self.send_response(405)
self.end_headers()
self.server.GETbusy = False
return True
def _showOutboxTimeline(self, authorized: bool, def _showOutboxTimeline(self, authorized: bool,
callingDomain: str, path: str, callingDomain: str, path: str,
baseDir: str, httpPrefix: str, baseDir: str, httpPrefix: str,
@ -10202,7 +10068,7 @@ class PubServer(BaseHTTPRequestHandler):
GETstartTime, GETtimings: {}) -> bool: GETstartTime, GETtimings: {}) -> bool:
"""Show a shared item image """Show a shared item image
""" """
if not pathIsImage(path): if not isImageFile(path):
self._404() self._404()
return True return True
@ -10252,7 +10118,7 @@ class PubServer(BaseHTTPRequestHandler):
if '/accounts/avatars/' not in path: if '/accounts/avatars/' not in path:
if '/accounts/headers/' not in path: if '/accounts/headers/' not in path:
return False return False
if not pathIsImage(path): if not isImageFile(path):
return False return False
if '/accounts/avatars/' in path: if '/accounts/avatars/' in path:
avatarStr = path.split('/accounts/avatars/')[1] avatarStr = path.split('/accounts/avatars/')[1]
@ -10385,7 +10251,7 @@ class PubServer(BaseHTTPRequestHandler):
# Various types of new post in the web interface # Various types of new post in the web interface
newPostEnd = ('newpost', 'newblog', 'newunlisted', newPostEnd = ('newpost', 'newblog', 'newunlisted',
'newfollowers', 'newdm', 'newreminder', 'newfollowers', 'newdm', 'newreminder',
'newevent', 'newreport', 'newquestion', 'newreport', 'newquestion',
'newshare') 'newshare')
for postType in newPostEnd: for postType in newPostEnd:
if path.endswith('/' + postType): if path.endswith('/' + postType):
@ -10578,44 +10444,6 @@ class PubServer(BaseHTTPRequestHandler):
return True return True
return False return False
def _editEvent(self, callingDomain: str, path: str,
httpPrefix: str, domain: str, domainFull: str,
baseDir: str, translate: {},
mediaInstance: bool,
cookie: str) -> bool:
"""Show edit event screen
"""
messageId = path.split('?editeventpost=')[1]
if '?' in messageId:
messageId = messageId.split('?')[0]
actor = path.split('?actor=')[1]
if '?' in actor:
actor = actor.split('?')[0]
nickname = getNicknameFromActor(path)
if nickname == actor:
# postUrl = \
# httpPrefix + '://' + \
# domainFull + '/users/' + nickname + \
# '/statuses/' + messageId
msg = None
# TODO
# htmlEditEvent(mediaInstance,
# translate,
# baseDir,
# httpPrefix,
# path,
# nickname, domain,
# postUrl)
if msg:
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
cookie, callingDomain)
self._write(msg)
self.server.GETbusy = False
return True
return False
def _getFollowingJson(self, baseDir: str, path: str, def _getFollowingJson(self, baseDir: str, path: str,
callingDomain: str, callingDomain: str,
httpPrefix: str, httpPrefix: str,
@ -11474,7 +11302,7 @@ class PubServer(BaseHTTPRequestHandler):
# if not authorized then show the login screen # if not authorized then show the login screen
if htmlGET and self.path != '/login' and \ if htmlGET and self.path != '/login' and \
not pathIsImage(self.path) and \ not isImageFile(self.path) and \
self.path != '/' and \ self.path != '/' and \
self.path != '/users/news/linksmobile' and \ self.path != '/users/news/linksmobile' and \
self.path != '/users/news/newswiremobile': self.path != '/users/news/newswiremobile':
@ -12498,21 +12326,6 @@ class PubServer(BaseHTTPRequestHandler):
self.server.GETbusy = False self.server.GETbusy = False
return return
# Edit an event
if authorized and \
'/tlevents' in self.path and \
'?editeventpost=' in self.path and \
'?actor=' in self.path:
if self._editEvent(callingDomain, self.path,
self.server.httpPrefix,
self.server.domain,
self.server.domainFull,
self.server.baseDir,
self.server.translate,
self.server.mediaInstance,
cookie):
return
# edit profile in web interface # edit profile in web interface
if self._editProfile(callingDomain, self.path, if self._editProfile(callingDomain, self.path,
self.server.translate, self.server.translate,
@ -12931,29 +12744,6 @@ class PubServer(BaseHTTPRequestHandler):
'show shares 2 done', 'show shares 2 done',
'show bookmarks 2 done') 'show bookmarks 2 done')
# get the events for a given person
if self.path.endswith('/tlevents') or \
'/tlevents?page=' in self.path or \
self.path.endswith('/events') or \
'/events?page=' in self.path:
if self._showEventsTimeline(authorized,
callingDomain, self.path,
self.server.baseDir,
self.server.httpPrefix,
self.server.domain,
self.server.domainFull,
self.server.port,
self.server.onionDomain,
self.server.i2pDomain,
GETstartTime, GETtimings,
self.server.proxyType,
cookie, self.server.debug):
return
self._benchmarkGETtimings(GETstartTime, GETtimings,
'show bookmarks 2 done',
'show events done')
# outbox timeline # outbox timeline
if self._showOutboxTimeline(authorized, if self._showOutboxTimeline(authorized,
callingDomain, self.path, callingDomain, self.path,
@ -13135,7 +12925,7 @@ class PubServer(BaseHTTPRequestHandler):
fileLength = -1 fileLength = -1
if '/media/' in self.path: if '/media/' in self.path:
if pathIsImage(self.path) or \ if isImageFile(self.path) or \
pathIsVideo(self.path) or \ pathIsVideo(self.path) or \
pathIsAudio(self.path): pathIsAudio(self.path):
mediaStr = self.path.split('/media/')[1] mediaStr = self.path.split('/media/')[1]
@ -13235,12 +13025,7 @@ class PubServer(BaseHTTPRequestHandler):
print('DEBUG: no media filename in POST') print('DEBUG: no media filename in POST')
if filename: if filename:
if filename.endswith('.png') or \ if isImageFile(filename):
filename.endswith('.jpg') or \
filename.endswith('.webp') or \
filename.endswith('.avif') or \
filename.endswith('.svg') or \
filename.endswith('.gif'):
postImageFilename = filename.replace('.temp', '') postImageFilename = filename.replace('.temp', '')
print('Removing metadata from ' + postImageFilename) print('Removing metadata from ' + postImageFilename)
city = getSpoofedCity(self.server.city, city = getSpoofedCity(self.server.city,
@ -13338,11 +13123,6 @@ class PubServer(BaseHTTPRequestHandler):
else: else:
commentsEnabled = True commentsEnabled = True
if not fields.get('privateEvent'):
privateEvent = False
else:
privateEvent = True
if postType == 'newpost': if postType == 'newpost':
if not fields.get('pinToProfile'): if not fields.get('pinToProfile'):
pinToProfile = False pinToProfile = False
@ -13425,14 +13205,20 @@ class PubServer(BaseHTTPRequestHandler):
print('WARN: blog posts must have content') print('WARN: blog posts must have content')
return -1 return -1
# submit button on newblog screen # submit button on newblog screen
followersOnly = False
saveToFile = False
clientToServer = False
city = None
messageJson = \ messageJson = \
createBlogPost(self.server.baseDir, nickname, createBlogPost(self.server.baseDir, nickname,
self.server.domain, self.server.port, self.server.domain, self.server.port,
self.server.httpPrefix, self.server.httpPrefix,
fields['message'], fields['message'],
False, False, False, commentsEnabled, followersOnly, saveToFile,
clientToServer, commentsEnabled,
filename, attachmentMediaType, filename, attachmentMediaType,
fields['imageDescription'], fields['imageDescription'],
city,
fields['replyTo'], fields['replyTo'], fields['replyTo'], fields['replyTo'],
fields['subject'], fields['subject'],
fields['schedulePost'], fields['schedulePost'],
@ -13545,13 +13331,17 @@ class PubServer(BaseHTTPRequestHandler):
self.server.baseDir, self.server.baseDir,
nickname, nickname,
self.server.domain) self.server.domain)
followersOnly = False
saveToFile = False
clientToServer = False
messageJson = \ messageJson = \
createUnlistedPost(self.server.baseDir, createUnlistedPost(self.server.baseDir,
nickname, nickname,
self.server.domain, self.server.port, self.server.domain, self.server.port,
self.server.httpPrefix, self.server.httpPrefix,
mentionsStr + fields['message'], mentionsStr + fields['message'],
False, False, False, commentsEnabled, followersOnly, saveToFile,
clientToServer, commentsEnabled,
filename, attachmentMediaType, filename, attachmentMediaType,
fields['imageDescription'], fields['imageDescription'],
city, city,
@ -13580,6 +13370,9 @@ class PubServer(BaseHTTPRequestHandler):
self.server.baseDir, self.server.baseDir,
nickname, nickname,
self.server.domain) self.server.domain)
followersOnly = True
saveToFile = False
clientToServer = False
messageJson = \ messageJson = \
createFollowersOnlyPost(self.server.baseDir, createFollowersOnlyPost(self.server.baseDir,
nickname, nickname,
@ -13587,7 +13380,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.port, self.server.port,
self.server.httpPrefix, self.server.httpPrefix,
mentionsStr + fields['message'], mentionsStr + fields['message'],
True, False, False, followersOnly, saveToFile,
clientToServer,
commentsEnabled, commentsEnabled,
filename, attachmentMediaType, filename, attachmentMediaType,
fields['imageDescription'], fields['imageDescription'],
@ -13612,64 +13406,6 @@ class PubServer(BaseHTTPRequestHandler):
return 1 return 1
else: else:
return -1 return -1
elif postType == 'newevent':
# A Mobilizon-type event is posted
# if there is no image dscription then make it the same
# as the event title
if not fields.get('imageDescription'):
fields['imageDescription'] = fields['subject']
# Events are public by default, with opt-in
# followers only status
if not fields.get('followersOnlyEvent'):
fields['followersOnlyEvent'] = False
if not fields.get('anonymousParticipationEnabled'):
anonymousParticipationEnabled = False
else:
anonymousParticipationEnabled = True
maximumAttendeeCapacity = 999999
if fields.get('maximumAttendeeCapacity'):
maximumAttendeeCapacity = \
int(fields['maximumAttendeeCapacity'])
city = getSpoofedCity(self.server.city,
self.server.baseDir,
nickname,
self.server.domain)
messageJson = \
createEventPost(self.server.baseDir,
nickname,
self.server.domain,
self.server.port,
self.server.httpPrefix,
mentionsStr + fields['message'],
privateEvent,
False, False, commentsEnabled,
filename, attachmentMediaType,
fields['imageDescription'],
city,
fields['subject'],
fields['schedulePost'],
fields['eventDate'],
fields['eventTime'],
fields['location'],
fields['category'],
fields['joinMode'],
fields['endDate'],
fields['endTime'],
maximumAttendeeCapacity,
fields['repliesModerationOption'],
anonymousParticipationEnabled,
fields['eventStatus'],
fields['ticketUrl'])
if messageJson:
if fields['schedulePost']:
return 1
if self._postToOutbox(messageJson, __version__, nickname):
return 1
else:
return -1
elif postType == 'newdm': elif postType == 'newdm':
messageJson = None messageJson = None
print('A DM was posted') print('A DM was posted')
@ -13678,6 +13414,9 @@ class PubServer(BaseHTTPRequestHandler):
self.server.baseDir, self.server.baseDir,
nickname, nickname,
self.server.domain) self.server.domain)
followersOnly = True
saveToFile = False
clientToServer = False
messageJson = \ messageJson = \
createDirectMessagePost(self.server.baseDir, createDirectMessagePost(self.server.baseDir,
nickname, nickname,
@ -13686,7 +13425,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.httpPrefix, self.server.httpPrefix,
mentionsStr + mentionsStr +
fields['message'], fields['message'],
True, False, False, followersOnly, saveToFile,
clientToServer,
commentsEnabled, commentsEnabled,
filename, attachmentMediaType, filename, attachmentMediaType,
fields['imageDescription'], fields['imageDescription'],
@ -13723,6 +13463,10 @@ class PubServer(BaseHTTPRequestHandler):
self.server.baseDir, self.server.baseDir,
nickname, nickname,
self.server.domain) self.server.domain)
followersOnly = True
saveToFile = False
clientToServer = False
commentsEnabled = False
messageJson = \ messageJson = \
createDirectMessagePost(self.server.baseDir, createDirectMessagePost(self.server.baseDir,
nickname, nickname,
@ -13730,7 +13474,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.port, self.server.port,
self.server.httpPrefix, self.server.httpPrefix,
mentionsStr + fields['message'], mentionsStr + fields['message'],
True, False, False, False, followersOnly, saveToFile,
clientToServer, commentsEnabled,
filename, attachmentMediaType, filename, attachmentMediaType,
fields['imageDescription'], fields['imageDescription'],
city, city,
@ -14172,7 +13917,6 @@ class PubServer(BaseHTTPRequestHandler):
if not self.path.endswith('confirm'): if not self.path.endswith('confirm'):
self.path = self.path.replace('/outbox/', '/outbox') self.path = self.path.replace('/outbox/', '/outbox')
self.path = self.path.replace('/tlblogs/', '/tlblogs') self.path = self.path.replace('/tlblogs/', '/tlblogs')
self.path = self.path.replace('/tlevents/', '/tlevents')
self.path = self.path.replace('/inbox/', '/inbox') self.path = self.path.replace('/inbox/', '/inbox')
self.path = self.path.replace('/shares/', '/shares') self.path = self.path.replace('/shares/', '/shares')
self.path = self.path.replace('/sharedInbox/', '/sharedInbox') self.path = self.path.replace('/sharedInbox/', '/sharedInbox')
@ -14510,7 +14254,7 @@ class PubServer(BaseHTTPRequestHandler):
# receive different types of post created by htmlNewPost # receive different types of post created by htmlNewPost
postTypes = ("newpost", "newblog", "newunlisted", "newfollowers", postTypes = ("newpost", "newblog", "newunlisted", "newfollowers",
"newdm", "newreport", "newshare", "newquestion", "newdm", "newreport", "newshare", "newquestion",
"editblogpost", "newreminder", "newevent") "editblogpost", "newreminder")
for currPostType in postTypes: for currPostType in postTypes:
if not authorized: if not authorized:
if self.server.debug: if self.server.debug:
@ -14520,8 +14264,6 @@ class PubServer(BaseHTTPRequestHandler):
postRedirect = self.server.defaultTimeline postRedirect = self.server.defaultTimeline
if currPostType == 'newshare': if currPostType == 'newshare':
postRedirect = 'shares' postRedirect = 'shares'
elif currPostType == 'newevent':
postRedirect = 'tlevents'
pageNumber = \ pageNumber = \
self._receiveNewPost(currPostType, self.path, self._receiveNewPost(currPostType, self.path,

View File

@ -1602,9 +1602,7 @@ if args.proxyPort:
setConfigParam(baseDir, 'proxyPort', proxyPort) setConfigParam(baseDir, 'proxyPort', proxyPort)
if args.gnunet: if args.gnunet:
httpPrefix = 'gnunet' httpPrefix = 'gnunet'
if args.dat: if args.dat or args.hyper:
httpPrefix = 'dat'
if args.hyper:
httpPrefix = 'hyper' httpPrefix = 'hyper'
if args.i2p: if args.i2p:
httpPrefix = 'http' httpPrefix = 'http'
@ -1645,7 +1643,7 @@ if args.followers:
if '/@' in args.followers or \ if '/@' in args.followers or \
'/users/' in args.followers or \ '/users/' in args.followers or \
args.followers.startswith('http') or \ args.followers.startswith('http') or \
args.followers.startswith('dat'): args.followers.startswith('hyper'):
# format: https://domain/@nick # format: https://domain/@nick
prefixes = getProtocolPrefixes() prefixes = getProtocolPrefixes()
for prefix in prefixes: for prefix in prefixes:
@ -2312,6 +2310,14 @@ if args.testdata:
testMediaType = None testMediaType = None
testImageDescription = None testImageDescription = None
testCity = 'London, England' testCity = 'London, England'
testInReplyTo = None
testInReplyToAtomUri = None
testSubject = None
testSchedulePost = False
testEventDate = None
testEventTime = None
testLocation = None
testIsArticle = False
createPublicPost(baseDir, nickname, domain, port, httpPrefix, createPublicPost(baseDir, nickname, domain, port, httpPrefix,
"like this is totally just a #test man", "like this is totally just a #test man",
@ -2320,7 +2326,11 @@ if args.testdata:
testC2S, testC2S,
testCommentsEnabled, testCommentsEnabled,
testAttachImageFilename, testAttachImageFilename,
testMediaType, testImageDescription, testCity) testMediaType, testImageDescription, testCity,
testInReplyTo, testInReplyToAtomUri,
testSubject, testSchedulePost,
testEventDate, testEventTime, testLocation,
testIsArticle)
createPublicPost(baseDir, nickname, domain, port, httpPrefix, createPublicPost(baseDir, nickname, domain, port, httpPrefix,
"Zoiks!!!", "Zoiks!!!",
testFollowersOnly, testFollowersOnly,
@ -2328,7 +2338,11 @@ if args.testdata:
testC2S, testC2S,
testCommentsEnabled, testCommentsEnabled,
testAttachImageFilename, testAttachImageFilename,
testMediaType, testImageDescription, testCity) testMediaType, testImageDescription, testCity,
testInReplyTo, testInReplyToAtomUri,
testSubject, testSchedulePost,
testEventDate, testEventTime, testLocation,
testIsArticle)
createPublicPost(baseDir, nickname, domain, port, httpPrefix, createPublicPost(baseDir, nickname, domain, port, httpPrefix,
"Hey scoob we need like a hundred more #milkshakes", "Hey scoob we need like a hundred more #milkshakes",
testFollowersOnly, testFollowersOnly,
@ -2336,7 +2350,11 @@ if args.testdata:
testC2S, testC2S,
testCommentsEnabled, testCommentsEnabled,
testAttachImageFilename, testAttachImageFilename,
testMediaType, testImageDescription, testCity) testMediaType, testImageDescription, testCity,
testInReplyTo, testInReplyToAtomUri,
testSubject, testSchedulePost,
testEventDate, testEventTime, testLocation,
testIsArticle)
createPublicPost(baseDir, nickname, domain, port, httpPrefix, createPublicPost(baseDir, nickname, domain, port, httpPrefix,
"Getting kinda spooky around here", "Getting kinda spooky around here",
testFollowersOnly, testFollowersOnly,
@ -2345,7 +2363,10 @@ if args.testdata:
testCommentsEnabled, testCommentsEnabled,
testAttachImageFilename, testAttachImageFilename,
testMediaType, testImageDescription, testCity, testMediaType, testImageDescription, testCity,
'someone') 'someone', testInReplyToAtomUri,
testSubject, testSchedulePost,
testEventDate, testEventTime, testLocation,
testIsArticle)
createPublicPost(baseDir, nickname, domain, port, httpPrefix, createPublicPost(baseDir, nickname, domain, port, httpPrefix,
"And they would have gotten away with it too" + "And they would have gotten away with it too" +
"if it wasn't for those pesky hackers", "if it wasn't for those pesky hackers",
@ -2354,7 +2375,11 @@ if args.testdata:
testC2S, testC2S,
testCommentsEnabled, testCommentsEnabled,
'img/logo.png', 'image/png', 'img/logo.png', 'image/png',
'Description of image', testCity) 'Description of image', testCity,
testInReplyTo, testInReplyToAtomUri,
testSubject, testSchedulePost,
testEventDate, testEventTime, testLocation,
testIsArticle)
createPublicPost(baseDir, nickname, domain, port, httpPrefix, createPublicPost(baseDir, nickname, domain, port, httpPrefix,
"man these centralized sites are like the worst!", "man these centralized sites are like the worst!",
testFollowersOnly, testFollowersOnly,
@ -2362,7 +2387,11 @@ if args.testdata:
testC2S, testC2S,
testCommentsEnabled, testCommentsEnabled,
testAttachImageFilename, testAttachImageFilename,
testMediaType, testImageDescription, testCity) testMediaType, testImageDescription, testCity,
testInReplyTo, testInReplyToAtomUri,
testSubject, testSchedulePost,
testEventDate, testEventTime, testLocation,
testIsArticle)
createPublicPost(baseDir, nickname, domain, port, httpPrefix, createPublicPost(baseDir, nickname, domain, port, httpPrefix,
"another mystery solved #test", "another mystery solved #test",
testFollowersOnly, testFollowersOnly,
@ -2370,7 +2399,11 @@ if args.testdata:
testC2S, testC2S,
testCommentsEnabled, testCommentsEnabled,
testAttachImageFilename, testAttachImageFilename,
testMediaType, testImageDescription, testCity) testMediaType, testImageDescription, testCity,
testInReplyTo, testInReplyToAtomUri,
testSubject, testSchedulePost,
testEventDate, testEventTime, testLocation,
testIsArticle)
createPublicPost(baseDir, nickname, domain, port, httpPrefix, createPublicPost(baseDir, nickname, domain, port, httpPrefix,
"let's go bowling", "let's go bowling",
testFollowersOnly, testFollowersOnly,
@ -2378,8 +2411,11 @@ if args.testdata:
testC2S, testC2S,
testCommentsEnabled, testCommentsEnabled,
testAttachImageFilename, testAttachImageFilename,
testMediaType, testImageDescription, testCity) testMediaType, testImageDescription, testCity,
testInReplyTo, testInReplyToAtomUri,
testSubject, testSchedulePost,
testEventDate, testEventTime, testLocation,
testIsArticle)
domainFull = domain + ':' + str(port) domainFull = domain + ':' + str(port)
clearFollows(baseDir, nickname, domain) clearFollows(baseDir, nickname, domain)
followPerson(baseDir, nickname, domain, 'maxboardroom', domainFull, followPerson(baseDir, nickname, domain, 'maxboardroom', domainFull,

View File

@ -338,7 +338,7 @@ def _getNoOfFollows(baseDir: str, nickname: str, domain: str,
not line.startswith('http'): not line.startswith('http'):
ctr += 1 ctr += 1
elif ((line.startswith('http') or elif ((line.startswith('http') or
line.startswith('dat')) and line.startswith('hyper')) and
hasUsersPath(line)): hasUsersPath(line)):
ctr += 1 ctr += 1
return ctr return ctr
@ -456,7 +456,7 @@ def getFollowingFeed(baseDir: str, domain: str, port: int, path: str,
line2.split('@')[0] line2.split('@')[0]
following['orderedItems'].append(url) following['orderedItems'].append(url)
elif ((line.startswith('http') or elif ((line.startswith('http') or
line.startswith('dat')) and line.startswith('hyper')) and
hasUsersPath(line)): hasUsersPath(line)):
# https://domain/users/nickname # https://domain/users/nickname
pageCtr += 1 pageCtr += 1

View File

@ -168,7 +168,7 @@ def _inboxStorePostToHtmlCache(recentPostsCache: {}, maxRecentPosts: int,
""" """
pageNumber = -999 pageNumber = -999
avatarUrl = None avatarUrl = None
if boxname != 'tlevents' and boxname != 'outbox': if boxname != 'outbox':
boxname = 'inbox' boxname = 'inbox'
individualPostAsHtml(True, recentPostsCache, maxRecentPosts, individualPostAsHtml(True, recentPostsCache, maxRecentPosts,
@ -2504,9 +2504,6 @@ def _inboxAfterInitial(recentPostsCache: {}, maxRecentPosts: int,
if isBlogPost(postJsonObject): if isBlogPost(postJsonObject):
# blogs index will be updated # blogs index will be updated
updateIndexList.append('tlblogs') updateIndexList.append('tlblogs')
elif isEventPost(postJsonObject):
# events index will be updated
updateIndexList.append('tlevents')
# get the avatar for a reply/announce # get the avatar for a reply/announce
_obtainAvatarForReplyPost(session, baseDir, _obtainAvatarForReplyPost(session, baseDir,

View File

@ -9,6 +9,7 @@ __module_group__ = "Timeline"
import os import os
import datetime import datetime
import subprocess
from random import randint from random import randint
from hashlib import sha1 from hashlib import sha1
from auth import createPassword from auth import createPassword
@ -245,6 +246,13 @@ def attachMedia(baseDir: str, httpPrefix: str,
} }
if mediaType.startswith('image/'): if mediaType.startswith('image/'):
attachmentJson['focialPoint'] = [0.0, 0.0] attachmentJson['focialPoint'] = [0.0, 0.0]
# find the dimensions of the image and add them as metadata
attachImageWidth, attachImageHeight = \
getImageDimensions(imageFilename)
if attachImageWidth and attachImageHeight:
attachmentJson['width'] = attachImageWidth
attachmentJson['height'] = attachImageHeight
postJson['attachment'] = [attachmentJson] postJson['attachment'] = [attachmentJson]
if baseDir: if baseDir:
@ -286,17 +294,6 @@ def archiveMedia(baseDir: str, archiveDirectory: str, maxWeeks=4) -> None:
break break
def pathIsImage(path: str) -> bool:
if path.endswith('.png') or \
path.endswith('.jpg') or \
path.endswith('.gif') or \
path.endswith('.svg') or \
path.endswith('.avif') or \
path.endswith('.webp'):
return True
return False
def pathIsVideo(path: str) -> bool: def pathIsVideo(path: str) -> bool:
if path.endswith('.ogv') or \ if path.endswith('.ogv') or \
path.endswith('.mp4'): path.endswith('.mp4'):
@ -309,3 +306,25 @@ def pathIsAudio(path: str) -> bool:
path.endswith('.mp3'): path.endswith('.mp3'):
return True return True
return False return False
def getImageDimensions(imageFilename: str) -> (int, int):
"""Returns the dimensions of an image file
"""
try:
result = subprocess.run(['identify', '-format', '"%wx%h"',
imageFilename], stdout=subprocess.PIPE)
except BaseException:
return None, None
if not result:
return None, None
dimensionsStr = result.stdout.decode('utf-8').replace('"', '')
if 'x' not in dimensionsStr:
return None, None
widthStr = dimensionsStr.split('x')[0]
if not widthStr.isdigit():
return None, None
heightStr = dimensionsStr.split('x')[1]
if not heightStr.isdigit():
return None, None
return int(widthStr), int(heightStr)

View File

@ -337,8 +337,6 @@ def postMessageToOutbox(session, translate: {},
if messageJson['object'].get('type'): if messageJson['object'].get('type'):
if messageJson['object']['type'] == 'Article': if messageJson['object']['type'] == 'Article':
outboxName = 'tlblogs' outboxName = 'tlblogs'
elif messageJson['object']['type'] == 'Event':
outboxName = 'tlevents'
savedFilename = \ savedFilename = \
savePostToBox(baseDir, savePostToBox(baseDir,

View File

@ -28,7 +28,6 @@ from posts import createNewsTimeline
from posts import createBlogsTimeline from posts import createBlogsTimeline
from posts import createFeaturesTimeline from posts import createFeaturesTimeline
from posts import createBookmarksTimeline from posts import createBookmarksTimeline
from posts import createEventsTimeline
from posts import createInbox from posts import createInbox
from posts import createOutbox from posts import createOutbox
from posts import createModeration from posts import createModeration
@ -746,8 +745,7 @@ def personBoxJson(recentPostsCache: {},
boxname != 'tlblogs' and boxname != 'tlnews' and \ boxname != 'tlblogs' and boxname != 'tlnews' and \
boxname != 'tlfeatures' and \ boxname != 'tlfeatures' and \
boxname != 'outbox' and boxname != 'moderation' and \ boxname != 'outbox' and boxname != 'moderation' and \
boxname != 'tlbookmarks' and boxname != 'bookmarks' and \ boxname != 'tlbookmarks' and boxname != 'bookmarks':
boxname != 'tlevents':
return None return None
if not '/' + boxname in path: if not '/' + boxname in path:
@ -796,12 +794,6 @@ def personBoxJson(recentPostsCache: {},
port, httpPrefix, port, httpPrefix,
noOfItems, headerOnly, noOfItems, headerOnly,
pageNumber) pageNumber)
elif boxname == 'tlevents':
return createEventsTimeline(recentPostsCache,
session, baseDir, nickname, domain,
port, httpPrefix,
noOfItems, headerOnly,
pageNumber)
elif boxname == 'tlreplies': elif boxname == 'tlreplies':
return createRepliesTimeline(recentPostsCache, return createRepliesTimeline(recentPostsCache,
session, baseDir, nickname, domain, session, baseDir, nickname, domain,
@ -1208,7 +1200,7 @@ def getActorJson(hostDomain: str, handle: str, http: bool, gnunet: bool,
if '/@' in handle or \ if '/@' in handle or \
'/users/' in handle or \ '/users/' in handle or \
handle.startswith('http') or \ handle.startswith('http') or \
handle.startswith('dat'): handle.startswith('hyper'):
# format: https://domain/@nick # format: https://domain/@nick
originalHandle = handle originalHandle = handle
if not hasUsersPath(originalHandle): if not hasUsersPath(originalHandle):

148
posts.py
View File

@ -14,7 +14,6 @@ import os
import shutil import shutil
import sys import sys
import time import time
import uuid
import random import random
from socket import error as SocketError from socket import error as SocketError
from time import gmtime, strftime from time import gmtime, strftime
@ -657,8 +656,7 @@ def deleteAllPosts(baseDir: str,
"""Deletes all posts for a person from inbox or outbox """Deletes all posts for a person from inbox or outbox
""" """
if boxname != 'inbox' and boxname != 'outbox' and \ if boxname != 'inbox' and boxname != 'outbox' and \
boxname != 'tlblogs' and boxname != 'tlnews' and \ boxname != 'tlblogs' and boxname != 'tlnews':
boxname != 'tlevents':
return return
boxDir = createPersonDir(nickname, domain, baseDir, boxname) boxDir = createPersonDir(nickname, domain, baseDir, boxname)
for deleteFilename in os.scandir(boxDir): for deleteFilename in os.scandir(boxDir):
@ -681,7 +679,6 @@ def savePostToBox(baseDir: str, httpPrefix: str, postId: str,
""" """
if boxname != 'inbox' and boxname != 'outbox' and \ if boxname != 'inbox' and boxname != 'outbox' and \
boxname != 'tlblogs' and boxname != 'tlnews' and \ boxname != 'tlblogs' and boxname != 'tlnews' and \
boxname != 'tlevents' and \
boxname != 'scheduled': boxname != 'scheduled':
return None return None
originalDomain = domain originalDomain = domain
@ -1066,18 +1063,18 @@ def _createPostBase(baseDir: str, nickname: str, domain: str, port: int,
mediaType: str, imageDescription: str, city: str, mediaType: str, imageDescription: str, city: str,
isModerationReport: bool, isModerationReport: bool,
isArticle: bool, isArticle: bool,
inReplyTo: str = None, inReplyTo: str,
inReplyToAtomUri: str = None, inReplyToAtomUri: str,
subject: str = None, schedulePost: bool = False, subject: str, schedulePost: bool,
eventDate: str = None, eventTime: str = None, eventDate: str, eventTime: str,
location: str = None, location: str,
eventUUID: str = None, category: str = None, eventUUID: str, category: str,
joinMode: str = None, joinMode: str,
endDate: str = None, endTime: str = None, endDate: str, endTime: str,
maximumAttendeeCapacity: int = None, maximumAttendeeCapacity: int,
repliesModerationOption: str = None, repliesModerationOption: str,
anonymousParticipationEnabled: bool = None, anonymousParticipationEnabled: bool,
eventStatus: str = None, ticketUrl: str = None) -> {}: eventStatus: str, ticketUrl: str) -> {}:
"""Creates a message """Creates a message
""" """
content = removeInvalidChars(content) content = removeInvalidChars(content)
@ -1244,9 +1241,6 @@ def _createPostBase(baseDir: str, nickname: str, domain: str, port: int,
if isArticle: if isArticle:
savePostToBox(baseDir, httpPrefix, newPostId, savePostToBox(baseDir, httpPrefix, newPostId,
nickname, domain, newPost, 'tlblogs') nickname, domain, newPost, 'tlblogs')
elif eventUUID:
savePostToBox(baseDir, httpPrefix, newPostId,
nickname, domain, newPost, 'tlevents')
else: else:
savePostToBox(baseDir, httpPrefix, newPostId, savePostToBox(baseDir, httpPrefix, newPostId,
nickname, domain, newPost, 'outbox') nickname, domain, newPost, 'outbox')
@ -1426,12 +1420,12 @@ def createPublicPost(baseDir: str,
clientToServer: bool, commentsEnabled: bool, clientToServer: bool, commentsEnabled: bool,
attachImageFilename: str, mediaType: str, attachImageFilename: str, mediaType: str,
imageDescription: str, city: str, imageDescription: str, city: str,
inReplyTo: str = None, inReplyTo: str,
inReplyToAtomUri: str = None, subject: str = None, inReplyToAtomUri: str, subject: str,
schedulePost: bool = False, schedulePost: bool,
eventDate: str = None, eventTime: str = None, eventDate: str, eventTime: str,
location: str = None, location: str,
isArticle: bool = False) -> {}: isArticle: bool) -> {}:
"""Public post """Public post
""" """
domainFull = getFullDomain(domain, port) domainFull = getFullDomain(domain, port)
@ -1501,10 +1495,10 @@ def createBlogPost(baseDir: str,
clientToServer: bool, commentsEnabled: bool, clientToServer: bool, commentsEnabled: bool,
attachImageFilename: str, mediaType: str, attachImageFilename: str, mediaType: str,
imageDescription: str, city: str, imageDescription: str, city: str,
inReplyTo: str = None, inReplyToAtomUri: str = None, inReplyTo: str, inReplyToAtomUri: str,
subject: str = None, schedulePost: bool = False, subject: str, schedulePost: bool,
eventDate: str = None, eventTime: str = None, eventDate: str, eventTime: str,
location: str = None) -> {}: location: str) -> {}:
blogJson = \ blogJson = \
createPublicPost(baseDir, createPublicPost(baseDir,
nickname, domain, port, httpPrefix, nickname, domain, port, httpPrefix,
@ -1600,10 +1594,10 @@ def createUnlistedPost(baseDir: str,
clientToServer: bool, commentsEnabled: bool, clientToServer: bool, commentsEnabled: bool,
attachImageFilename: str, mediaType: str, attachImageFilename: str, mediaType: str,
imageDescription: str, city: str, imageDescription: str, city: str,
inReplyTo: str = None, inReplyToAtomUri: str = None, inReplyTo: str, inReplyToAtomUri: str,
subject: str = None, schedulePost: bool = False, subject: str, schedulePost: bool,
eventDate: str = None, eventTime: str = None, eventDate: str, eventTime: str,
location: str = None) -> {}: location: str) -> {}:
"""Unlisted post. This has the #Public and followers links inverted. """Unlisted post. This has the #Public and followers links inverted.
""" """
domainFull = getFullDomain(domain, port) domainFull = getFullDomain(domain, port)
@ -1630,11 +1624,11 @@ def createFollowersOnlyPost(baseDir: str,
clientToServer: bool, commentsEnabled: bool, clientToServer: bool, commentsEnabled: bool,
attachImageFilename: str, mediaType: str, attachImageFilename: str, mediaType: str,
imageDescription: str, city: str, imageDescription: str, city: str,
inReplyTo: str = None, inReplyTo: str,
inReplyToAtomUri: str = None, inReplyToAtomUri: str,
subject: str = None, schedulePost: bool = False, subject: str, schedulePost: bool,
eventDate: str = None, eventTime: str = None, eventDate: str, eventTime: str,
location: str = None) -> {}: location: str) -> {}:
"""Followers only post """Followers only post
""" """
domainFull = getFullDomain(domain, port) domainFull = getFullDomain(domain, port)
@ -1653,57 +1647,6 @@ def createFollowersOnlyPost(baseDir: str,
None, None, None, None, None) None, None, None, None, None)
def createEventPost(baseDir: str,
nickname: str, domain: str, port: int,
httpPrefix: str,
content: str, followersOnly: bool,
saveToFile: bool,
clientToServer: bool, commentsEnabled: bool,
attachImageFilename: str, mediaType: str,
imageDescription: str, city: str,
subject: str = None, schedulePost: str = False,
eventDate: str = None, eventTime: str = None,
location: str = None, category: str = None,
joinMode: str = None,
endDate: str = None, endTime: str = None,
maximumAttendeeCapacity: int = None,
repliesModerationOption: str = None,
anonymousParticipationEnabled: bool = None,
eventStatus: str = None, ticketUrl: str = None) -> {}:
"""Mobilizon-type Event post
"""
if not attachImageFilename:
print('Event has no attached image')
return None
if not category:
print('Event has no category')
return None
domainFull = getFullDomain(domain, port)
# create event uuid
eventUUID = str(uuid.uuid1())
toStr1 = 'https://www.w3.org/ns/activitystreams#Public'
toStr2 = httpPrefix + '://' + domainFull + '/users/' + \
nickname + '/followers',
if followersOnly:
toStr1 = toStr2
toStr2 = None
return _createPostBase(baseDir, nickname, domain, port,
toStr1, toStr2,
httpPrefix, content, followersOnly, saveToFile,
clientToServer, commentsEnabled,
attachImageFilename, mediaType,
imageDescription, city,
False, False, None, None, subject,
schedulePost, eventDate, eventTime, location,
eventUUID, category, joinMode,
endDate, endTime, maximumAttendeeCapacity,
repliesModerationOption,
anonymousParticipationEnabled,
eventStatus, ticketUrl)
def getMentionedPeople(baseDir: str, httpPrefix: str, def getMentionedPeople(baseDir: str, httpPrefix: str,
content: str, domain: str, debug: bool) -> []: content: str, domain: str, debug: bool) -> []:
"""Extracts a list of mentioned actors from the given message content """Extracts a list of mentioned actors from the given message content
@ -1747,12 +1690,12 @@ def createDirectMessagePost(baseDir: str,
commentsEnabled: bool, commentsEnabled: bool,
attachImageFilename: str, mediaType: str, attachImageFilename: str, mediaType: str,
imageDescription: str, city: str, imageDescription: str, city: str,
inReplyTo: str = None, inReplyTo: str,
inReplyToAtomUri: str = None, inReplyToAtomUri: str,
subject: str = None, debug: bool = False, subject: str, debug: bool,
schedulePost: bool = False, schedulePost: bool,
eventDate: str = None, eventTime: str = None, eventDate: str, eventTime: str,
location: str = None) -> {}: location: str) -> {}:
"""Direct Message post """Direct Message post
""" """
content = resolvePetnames(baseDir, nickname, domain, content) content = resolvePetnames(baseDir, nickname, domain, content)
@ -1825,7 +1768,7 @@ def createReportPost(baseDir: str,
if moderatorActor not in moderatorsList: if moderatorActor not in moderatorsList:
moderatorsList.append(moderatorActor) moderatorsList.append(moderatorActor)
continue continue
if line.startswith('http') or line.startswith('dat'): if line.startswith('http') or line.startswith('hyper'):
# must be a local address - no remote moderators # must be a local address - no remote moderators
if '://' + domainFull + '/' in line: if '://' + domainFull + '/' in line:
if line not in moderatorsList: if line not in moderatorsList:
@ -2835,16 +2778,6 @@ def createBookmarksTimeline(session, baseDir: str, nickname: str, domain: str,
True, 0, False, 0, pageNumber) True, 0, False, 0, pageNumber)
def createEventsTimeline(recentPostsCache: {},
session, baseDir: str, nickname: str, domain: str,
port: int, httpPrefix: str, itemsPerPage: int,
headerOnly: bool, pageNumber: int = None) -> {}:
return _createBoxIndexed(recentPostsCache, session, baseDir, 'tlevents',
nickname, domain,
port, httpPrefix, itemsPerPage, headerOnly,
True, 0, False, 0, pageNumber)
def createDMTimeline(recentPostsCache: {}, def createDMTimeline(recentPostsCache: {},
session, baseDir: str, nickname: str, domain: str, session, baseDir: str, nickname: str, domain: str,
port: int, httpPrefix: str, itemsPerPage: int, port: int, httpPrefix: str, itemsPerPage: int,
@ -3179,8 +3112,7 @@ def _createBoxIndexed(recentPostsCache: {},
boxname != 'tlblogs' and boxname != 'tlnews' and \ boxname != 'tlblogs' and boxname != 'tlnews' and \
boxname != 'tlfeatures' and \ boxname != 'tlfeatures' and \
boxname != 'outbox' and boxname != 'tlbookmarks' and \ boxname != 'outbox' and boxname != 'tlbookmarks' and \
boxname != 'bookmarks' and \ boxname != 'bookmarks':
boxname != 'tlevents':
return None return None
# bookmarks and events timelines are like the inbox # bookmarks and events timelines are like the inbox

View File

@ -371,7 +371,7 @@ def load_document(url):
# validate URL # validate URL
pieces = urllib_parse.urlparse(url) pieces = urllib_parse.urlparse(url)
if (not all([pieces.scheme, pieces.netloc]) or if (not all([pieces.scheme, pieces.netloc]) or
pieces.scheme not in ['http', 'https', 'dat'] or pieces.scheme not in ['http', 'https', 'hyper'] or
set(pieces.netloc) > set( set(pieces.netloc) > set(
string.ascii_letters + string.digits + '-.:')): string.ascii_letters + string.digits + '-.:')):
raise JsonLdError( raise JsonLdError(

136
tests.py
View File

@ -86,6 +86,7 @@ from announce import sendAnnounceViaServer
from city import parseNogoString from city import parseNogoString
from city import spoofGeolocation from city import spoofGeolocation
from city import pointInNogo from city import pointInNogo
from media import getImageDimensions
from media import getMediaPath from media import getMediaPath
from media import getAttachmentMediaType from media import getAttachmentMediaType
from delete import sendDeleteViaServer from delete import sendDeleteViaServer
@ -476,6 +477,14 @@ def createServerAlice(path: str, domain: str, port: int,
testMediaType = None testMediaType = None
testImageDescription = None testImageDescription = None
testCity = 'London, England' testCity = 'London, England'
testInReplyTo = None
testInReplyToAtomUri = None
testSubject = None
testSchedulePost = False
testEventDate = None
testEventTime = None
testLocation = None
testIsArticle = False
createPublicPost(path, nickname, domain, port, httpPrefix, createPublicPost(path, nickname, domain, port, httpPrefix,
"No wise fish would go anywhere without a porpoise", "No wise fish would go anywhere without a porpoise",
testFollowersOnly, testFollowersOnly,
@ -484,7 +493,11 @@ def createServerAlice(path: str, domain: str, port: int,
testCommentsEnabled, testCommentsEnabled,
testAttachImageFilename, testAttachImageFilename,
testMediaType, testMediaType,
testImageDescription, testCity) testImageDescription, testCity,
testInReplyTo, testInReplyToAtomUri,
testSubject, testSchedulePost,
testEventDate, testEventTime, testLocation,
testIsArticle)
createPublicPost(path, nickname, domain, port, httpPrefix, createPublicPost(path, nickname, domain, port, httpPrefix,
"Curiouser and curiouser!", "Curiouser and curiouser!",
testFollowersOnly, testFollowersOnly,
@ -493,7 +506,11 @@ def createServerAlice(path: str, domain: str, port: int,
testCommentsEnabled, testCommentsEnabled,
testAttachImageFilename, testAttachImageFilename,
testMediaType, testMediaType,
testImageDescription, testCity) testImageDescription, testCity,
testInReplyTo, testInReplyToAtomUri,
testSubject, testSchedulePost,
testEventDate, testEventTime, testLocation,
testIsArticle)
createPublicPost(path, nickname, domain, port, httpPrefix, createPublicPost(path, nickname, domain, port, httpPrefix,
"In the gardens of memory, in the palace " + "In the gardens of memory, in the palace " +
"of dreams, that is where you and I shall meet", "of dreams, that is where you and I shall meet",
@ -503,7 +520,11 @@ def createServerAlice(path: str, domain: str, port: int,
testCommentsEnabled, testCommentsEnabled,
testAttachImageFilename, testAttachImageFilename,
testMediaType, testMediaType,
testImageDescription, testCity) testImageDescription, testCity,
testInReplyTo, testInReplyToAtomUri,
testSubject, testSchedulePost,
testEventDate, testEventTime, testLocation,
testIsArticle)
global testServerAliceRunning global testServerAliceRunning
testServerAliceRunning = True testServerAliceRunning = True
maxMentions = 10 maxMentions = 10
@ -581,6 +602,14 @@ def createServerBob(path: str, domain: str, port: int,
testImageDescription = None testImageDescription = None
testMediaType = None testMediaType = None
testCity = 'London, England' testCity = 'London, England'
testInReplyTo = None
testInReplyToAtomUri = None
testSubject = None
testSchedulePost = False
testEventDate = None
testEventTime = None
testLocation = None
testIsArticle = False
createPublicPost(path, nickname, domain, port, httpPrefix, createPublicPost(path, nickname, domain, port, httpPrefix,
"It's your life, live it your way.", "It's your life, live it your way.",
testFollowersOnly, testFollowersOnly,
@ -589,7 +618,11 @@ def createServerBob(path: str, domain: str, port: int,
testCommentsEnabled, testCommentsEnabled,
testAttachImageFilename, testAttachImageFilename,
testMediaType, testMediaType,
testImageDescription, testCity) testImageDescription, testCity,
testInReplyTo, testInReplyToAtomUri,
testSubject, testSchedulePost,
testEventDate, testEventTime, testLocation,
testIsArticle)
createPublicPost(path, nickname, domain, port, httpPrefix, createPublicPost(path, nickname, domain, port, httpPrefix,
"One of the things I've realised is that " + "One of the things I've realised is that " +
"I am very simple", "I am very simple",
@ -599,7 +632,11 @@ def createServerBob(path: str, domain: str, port: int,
testCommentsEnabled, testCommentsEnabled,
testAttachImageFilename, testAttachImageFilename,
testMediaType, testMediaType,
testImageDescription, testCity) testImageDescription, testCity,
testInReplyTo, testInReplyToAtomUri,
testSubject, testSchedulePost,
testEventDate, testEventTime, testLocation,
testIsArticle)
createPublicPost(path, nickname, domain, port, httpPrefix, createPublicPost(path, nickname, domain, port, httpPrefix,
"Quantum physics is a bit of a passion of mine", "Quantum physics is a bit of a passion of mine",
testFollowersOnly, testFollowersOnly,
@ -608,7 +645,11 @@ def createServerBob(path: str, domain: str, port: int,
testCommentsEnabled, testCommentsEnabled,
testAttachImageFilename, testAttachImageFilename,
testMediaType, testMediaType,
testImageDescription, testCity) testImageDescription, testCity,
testInReplyTo, testInReplyToAtomUri,
testSubject, testSchedulePost,
testEventDate, testEventTime, testLocation,
testIsArticle)
global testServerBobRunning global testServerBobRunning
testServerBobRunning = True testServerBobRunning = True
maxMentions = 10 maxMentions = 10
@ -792,6 +833,10 @@ def testPostMessageBetweenServers():
alicePersonCache = {} alicePersonCache = {}
aliceCachedWebfingers = {} aliceCachedWebfingers = {}
attachedImageFilename = baseDir + '/img/logo.png' attachedImageFilename = baseDir + '/img/logo.png'
testImageWidth, testImageHeight = \
getImageDimensions(attachedImageFilename)
assert testImageWidth
assert testImageHeight
mediaType = getAttachmentMediaType(attachedImageFilename) mediaType = getAttachmentMediaType(attachedImageFilename)
attachedImageDescription = 'Logo' attachedImageDescription = 'Logo'
isArticle = False isArticle = False
@ -875,6 +920,20 @@ def testPostMessageBetweenServers():
assert 'Why is a mouse when it spins?' in \ assert 'Why is a mouse when it spins?' in \
receivedJson['object']['content'] receivedJson['object']['content']
assert 'यह एक परीक्षण है' in receivedJson['object']['content'] assert 'यह एक परीक्षण है' in receivedJson['object']['content']
print('Check that message received from Alice contains an attachment')
assert receivedJson['object']['attachment']
assert len(receivedJson['object']['attachment']) == 1
attached = receivedJson['object']['attachment'][0]
pprint(attached)
assert attached.get('type')
assert attached.get('url')
assert attached['mediaType'] == 'image/png'
assert '/media/' in attached['url']
assert attached['url'].endswith('.png')
assert attached.get('width')
assert attached.get('height')
assert attached['width'] > 0
assert attached['height'] > 0
print('\n\n*******************************************************') print('\n\n*******************************************************')
print("Bob likes Alice's post") print("Bob likes Alice's post")
@ -1416,10 +1475,28 @@ def _testCreatePerson():
setBio(baseDir, nickname, domain, 'Randomly roaming in your backyard') setBio(baseDir, nickname, domain, 'Randomly roaming in your backyard')
archivePostsForPerson(nickname, domain, baseDir, 'inbox', None, {}, 4) archivePostsForPerson(nickname, domain, baseDir, 'inbox', None, {}, 4)
archivePostsForPerson(nickname, domain, baseDir, 'outbox', None, {}, 4) archivePostsForPerson(nickname, domain, baseDir, 'outbox', None, {}, 4)
testInReplyTo = None
testInReplyToAtomUri = None
testSubject = None
testSchedulePost = False
testEventDate = None
testEventTime = None
testLocation = None
testIsArticle = False
content = "G'day world!"
followersOnly = False
saveToFile = True
commentsEnabled = True
attachImageFilename = None
mediaType = None
createPublicPost(baseDir, nickname, domain, port, httpPrefix, createPublicPost(baseDir, nickname, domain, port, httpPrefix,
"G'day world!", False, True, clientToServer, content, followersOnly, saveToFile, clientToServer,
True, None, None, None, None, commentsEnabled, attachImageFilename, mediaType,
'Not suitable for Vogons', 'London, England') 'Not suitable for Vogons', 'London, England',
testInReplyTo, testInReplyToAtomUri,
testSubject, testSchedulePost,
testEventDate, testEventTime, testLocation,
testIsArticle)
os.chdir(currDir) os.chdir(currDir)
shutil.rmtree(baseDir) shutil.rmtree(baseDir)
@ -2802,12 +2879,23 @@ def _testReplyToPublicPost() -> None:
mediaType = None mediaType = None
imageDescription = 'Some description' imageDescription = 'Some description'
city = 'London, England' city = 'London, England'
testInReplyToAtomUri = None
testSubject = None
testSchedulePost = False
testEventDate = None
testEventTime = None
testLocation = None
testIsArticle = False
reply = \ reply = \
createPublicPost(baseDir, nickname, domain, port, httpPrefix, createPublicPost(baseDir, nickname, domain, port, httpPrefix,
content, followersOnly, saveToFile, content, followersOnly, saveToFile,
clientToServer, commentsEnabled, clientToServer, commentsEnabled,
attachImageFilename, mediaType, attachImageFilename, mediaType,
imageDescription, city, postId) imageDescription, city, postId,
testInReplyToAtomUri,
testSubject, testSchedulePost,
testEventDate, testEventTime, testLocation,
testIsArticle)
# print(str(reply)) # print(str(reply))
assert reply['object']['content'] == \ assert reply['object']['content'] == \
'<p><span class=\"h-card\">' + \ '<p><span class=\"h-card\">' + \
@ -3365,16 +3453,30 @@ def _testLinksWithinPost() -> None:
saveToFile = False saveToFile = False
clientToServer = False clientToServer = False
commentsEnabled = True commentsEnabled = True
attachImageFilename = None
mediaType = None mediaType = None
imageDescription = None imageDescription = None
city = 'London, England' city = 'London, England'
testInReplyTo = None
testInReplyToAtomUri = None
testSubject = None
testSchedulePost = False
testEventDate = None
testEventTime = None
testLocation = None
testIsArticle = False
postJsonObject = \ postJsonObject = \
createPublicPost(baseDir, nickname, domain, port, httpPrefix, createPublicPost(baseDir, nickname, domain, port, httpPrefix,
content, followersOnly, saveToFile, content, followersOnly, saveToFile,
clientToServer, commentsEnabled, clientToServer, commentsEnabled,
mediaType, imageDescription, city, attachImageFilename, mediaType,
False, None) imageDescription, city,
testInReplyTo, testInReplyToAtomUri,
testSubject, testSchedulePost,
testEventDate, testEventTime, testLocation,
testIsArticle)
assert postJsonObject['object']['content'] == \ assert postJsonObject['object']['content'] == \
'<p>This is a test post with links.<br><br>' + \ '<p>This is a test post with links.<br><br>' + \
'<a href="ftp://ftp.ncdc.noaa.gov/pub/data/ghcn/v4/" ' + \ '<a href="ftp://ftp.ncdc.noaa.gov/pub/data/ghcn/v4/" ' + \
@ -3400,8 +3502,14 @@ def _testLinksWithinPost() -> None:
postJsonObject = \ postJsonObject = \
createPublicPost(baseDir, nickname, domain, port, httpPrefix, createPublicPost(baseDir, nickname, domain, port, httpPrefix,
content, content,
False, False, False, True, False, False,
None, None, False, None) False, True,
None, None,
False, None,
testInReplyTo, testInReplyToAtomUri,
testSubject, testSchedulePost,
testEventDate, testEventTime, testLocation,
testIsArticle)
assert postJsonObject['object']['content'] == content assert postJsonObject['object']['content'] == content

View File

@ -270,6 +270,15 @@ def getImageFormats() -> str:
return imageFormats return imageFormats
def isImageFile(filename: str) -> bool:
"""Is the given filename an image?
"""
for ext in getImageExtensions():
if filename.endswith('.' + ext):
return True
return False
def getMediaFormats() -> str: def getMediaFormats() -> str:
"""Returns a string of permissable media formats """Returns a string of permissable media formats
used when selecting an attachment for a new post used when selecting an attachment for a new post
@ -1130,7 +1139,7 @@ def locatePost(baseDir: str, nickname: str, domain: str,
postUrl = postUrl + '.' + extension postUrl = postUrl + '.' + extension
# search boxes # search boxes
boxes = ('inbox', 'outbox', 'tlblogs', 'tlevents') boxes = ('inbox', 'outbox', 'tlblogs')
accountDir = baseDir + '/accounts/' + nickname + '@' + domain + '/' accountDir = baseDir + '/accounts/' + nickname + '@' + domain + '/'
for boxName in boxes: for boxName in boxes:
postFilename = accountDir + boxName + '/' + postUrl postFilename = accountDir + boxName + '/' + postUrl
@ -1392,7 +1401,7 @@ def _isReservedName(nickname: str) -> bool:
'public', 'followers', 'category', 'public', 'followers', 'category',
'channel', 'calendar', 'channel', 'calendar',
'tlreplies', 'tlmedia', 'tlblogs', 'tlreplies', 'tlmedia', 'tlblogs',
'tlevents', 'tlblogs', 'tlfeatures', 'tlblogs', 'tlfeatures',
'moderation', 'moderationaction', 'moderation', 'moderationaction',
'activity', 'undo', 'pinned', 'activity', 'undo', 'pinned',
'reply', 'replies', 'question', 'like', 'reply', 'replies', 'question', 'like',

View File

@ -129,20 +129,6 @@ def _htmlTimelineNewPost(manuallyApproveFollowers: bool,
'<a href="' + usersPath + '/newblog">' + \ '<a href="' + usersPath + '/newblog">' + \
'<button class="button"><span>' + \ '<button class="button"><span>' + \
translate['Post'] + '</span></button></a>' translate['Post'] + '</span></button></a>'
elif boxName == 'tlevents':
if not iconsAsButtons:
newPostButtonStr += \
'<a class="imageAnchor" href="' + usersPath + \
'/newevent?nodropdown"><img loading="lazy" src="/' + \
'icons/newpost.png" title="' + \
translate['Create a new event'] + '" alt="| ' + \
translate['Create a new event'] + \
'" class="timelineicon"/></a>\n'
else:
newPostButtonStr += \
'<a href="' + usersPath + '/newevent?nodropdown">' + \
'<button class="button"><span>' + \
translate['Post'] + '</span></button></a>'
elif boxName == 'tlshares': elif boxName == 'tlshares':
if not iconsAsButtons: if not iconsAsButtons:
newPostButtonStr += \ newPostButtonStr += \
@ -500,8 +486,6 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
sharesButton = 'buttonselectedhighlighted' sharesButton = 'buttonselectedhighlighted'
elif boxName == 'tlbookmarks' or boxName == 'bookmarks': elif boxName == 'tlbookmarks' or boxName == 'bookmarks':
bookmarksButton = 'buttonselected' bookmarksButton = 'buttonselected'
# elif boxName == 'tlevents':
# eventsButton = 'buttonselected'
# get the full domain, including any port number # get the full domain, including any port number
fullDomain = getFullDomain(domain, port) fullDomain = getFullDomain(domain, port)
@ -559,11 +543,6 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
'<a href="' + usersPath + '/tlbookmarks"><button class="' + \ '<a href="' + usersPath + '/tlbookmarks"><button class="' + \
bookmarksButton + '"><span>' + translate['Bookmarks'] + \ bookmarksButton + '"><span>' + translate['Bookmarks'] + \
'</span></button></a>' '</span></button></a>'
#
# eventsButtonStr = \
# '<a href="' + usersPath + '/tlevents"><button class="' + \
# eventsButton + '"><span>' + translate['Events'] + \
# '</span></button></a>'
instanceTitle = \ instanceTitle = \
getConfigParam(baseDir, 'instanceTitle') getConfigParam(baseDir, 'instanceTitle')
@ -1054,51 +1033,6 @@ def htmlBookmarks(cssCache: {}, defaultTimeline: str,
accessKeys) accessKeys)
def htmlEvents(cssCache: {}, defaultTimeline: str,
recentPostsCache: {}, maxRecentPosts: int,
translate: {}, pageNumber: int, itemsPerPage: int,
session, baseDir: str,
cachedWebfingers: {}, personCache: {},
nickname: str, domain: str, port: int, bookmarksJson: {},
allowDeletion: bool,
httpPrefix: str, projectVersion: str,
minimal: bool, YTReplacementDomain: str,
showPublishedDateOnly: bool,
newswire: {}, positiveVoting: bool,
showPublishAsIcon: bool,
fullWidthTimelineButtonHeader: bool,
iconsAsButtons: bool,
rssIconAtTop: bool,
publishButtonAtTop: bool,
authorized: bool, theme: str,
peertubeInstances: [],
allowLocalNetworkAccess: bool,
textModeBanner: str,
accessKeys: {}) -> str:
"""Show the events as html
"""
manuallyApproveFollowers = \
followerApprovalActive(baseDir, nickname, domain)
return htmlTimeline(cssCache, defaultTimeline,
recentPostsCache, maxRecentPosts,
translate, pageNumber,
itemsPerPage, session, baseDir,
cachedWebfingers, personCache,
nickname, domain, port, bookmarksJson,
'tlevents', allowDeletion,
httpPrefix, projectVersion, manuallyApproveFollowers,
minimal, YTReplacementDomain,
showPublishedDateOnly,
newswire, False, False,
positiveVoting, showPublishAsIcon,
fullWidthTimelineButtonHeader,
iconsAsButtons, rssIconAtTop, publishButtonAtTop,
authorized, None, theme, peertubeInstances,
allowLocalNetworkAccess, textModeBanner,
accessKeys)
def htmlInboxDMs(cssCache: {}, defaultTimeline: str, def htmlInboxDMs(cssCache: {}, defaultTimeline: str,
recentPostsCache: {}, maxRecentPosts: int, recentPostsCache: {}, maxRecentPosts: int,
translate: {}, pageNumber: int, itemsPerPage: int, translate: {}, pageNumber: int, itemsPerPage: int,