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

merge-requests/30/head
Bob Mottram 2020-12-24 17:41:51 +00:00
commit b754b13fd8
42 changed files with 1043 additions and 483 deletions

View File

@ -7,6 +7,7 @@ __email__ = "bob@freedombone.net"
__status__ = "Production"
import os
from utils import hasUsersPath
from utils import getFullDomain
from utils import urlPermitted
from utils import getDomainFromActor
@ -182,10 +183,7 @@ def receiveAcceptReject(session, baseDir: str,
if debug:
print('DEBUG: ' + messageJson['type'] + ' has no actor')
return False
if '/users/' not in messageJson['actor'] and \
'/accounts/' not in messageJson['actor'] and \
'/channel/' not in messageJson['actor'] and \
'/profile/' not in messageJson['actor']:
if not hasUsersPath(messageJson['actor']):
if debug:
print('DEBUG: "users" or "profile" missing from actor in ' +
messageJson['type'] + '. Assuming single user instance.')

View File

@ -6,6 +6,7 @@ __maintainer__ = "Bob Mottram"
__email__ = "bob@freedombone.net"
__status__ = "Production"
from utils import hasUsersPath
from utils import getFullDomain
from utils import getStatusNumber
from utils import createOutboxDir
@ -143,10 +144,7 @@ def createAnnounce(session, baseDir: str, federationList: [],
announceNickname = None
announceDomain = None
announcePort = None
if '/users/' in objectUrl or \
'/accounts/' in objectUrl or \
'/channel/' in objectUrl or \
'/profile/' in objectUrl:
if hasUsersPath(objectUrl):
announceNickname = getNicknameFromActor(objectUrl)
announceDomain, announcePort = getDomainFromActor(objectUrl)

View File

@ -12,6 +12,7 @@ import binascii
import os
import secrets
from utils import isSystemAccount
from utils import hasUsersPath
def _hashPassword(password: str) -> str:
@ -89,10 +90,7 @@ def authorizeBasic(baseDir: str, path: str, authHeader: str,
print('DEBUG: basic auth - Authorixation header does not ' +
'contain a space character')
return False
if '/users/' not in path and \
'/accounts/' not in path and \
'/channel/' not in path and \
'/profile/' not in path:
if not hasUsersPath(path):
if debug:
print('DEBUG: basic auth - ' +
'path for Authorization does not contain a user')

View File

@ -7,6 +7,7 @@ __email__ = "bob@freedombone.net"
__status__ = "Production"
import os
from utils import hasUsersPath
from utils import getFullDomain
from utils import removeIdEnding
from utils import isEvil
@ -246,10 +247,7 @@ def outboxBlock(baseDir: str, httpPrefix: str,
if debug:
print('DEBUG: c2s block object is not a status')
return
if '/users/' not in messageId and \
'/accounts/' not in messageId and \
'/channel/' not in messageId and \
'/profile/' not in messageId:
if not hasUsersPath(messageId):
if debug:
print('DEBUG: c2s block object has no nickname')
return
@ -321,10 +319,7 @@ def outboxUndoBlock(baseDir: str, httpPrefix: str,
if debug:
print('DEBUG: c2s undo block object is not a status')
return
if '/users/' not in messageId and \
'/accounts/' not in messageId and \
'/channel/' not in messageId and \
'/profile/' not in messageId:
if not hasUsersPath(messageId):
if debug:
print('DEBUG: c2s undo block object has no nickname')
return

21
blog.py
View File

@ -158,6 +158,7 @@ def _htmlBlogPostContent(authorized: bool,
nickname: str, domain: str, domainFull: str,
postJsonObject: {},
handle: str, restrictToDomain: bool,
peertubeInstances: [],
blogSeparator='<hr>') -> str:
"""Returns the content for a single blog post
"""
@ -231,7 +232,8 @@ def _htmlBlogPostContent(authorized: bool,
if postJsonObject['object'].get('content'):
contentStr = addEmbeddedElements(translate,
postJsonObject['object']['content'])
postJsonObject['object']['content'],
peertubeInstances)
if postJsonObject['object'].get('tag'):
contentStr = replaceEmojiFromTags(contentStr,
postJsonObject['object']['tag'],
@ -375,7 +377,8 @@ def _htmlBlogRemoveCwButton(blogStr: str, translate: {}) -> str:
def htmlBlogPost(authorized: bool,
baseDir: str, httpPrefix: str, translate: {},
nickname: str, domain: str, domainFull: str,
postJsonObject: {}) -> str:
postJsonObject: {},
peertubeInstances: []) -> str:
"""Returns a html blog post
"""
blogStr = ''
@ -390,7 +393,8 @@ def htmlBlogPost(authorized: bool,
httpPrefix, translate,
nickname, domain,
domainFull, postJsonObject,
None, False)
None, False,
peertubeInstances)
# show rss links
blogStr += '<p class="rssfeed">'
@ -417,7 +421,8 @@ def htmlBlogPost(authorized: bool,
def htmlBlogPage(authorized: bool, session,
baseDir: str, httpPrefix: str, translate: {},
nickname: str, domain: str, port: int,
noOfItems: int, pageNumber: int) -> str:
noOfItems: int, pageNumber: int,
peertubeInstances: []) -> str:
"""Returns a html blog page containing posts
"""
if ' ' in nickname or '@' in nickname or \
@ -477,7 +482,8 @@ def htmlBlogPage(authorized: bool, session,
httpPrefix, translate,
nickname, domain,
domainFull, item,
None, True)
None, True,
peertubeInstances)
if len(timelineJson['orderedItems']) >= noOfItems:
blogStr += navigateStr
@ -638,7 +644,8 @@ def _singleBlogAccountNickname(baseDir: str) -> str:
def htmlBlogView(authorized: bool,
session, baseDir: str, httpPrefix: str,
translate: {}, domain: str, port: int,
noOfItems: int) -> str:
noOfItems: int,
peertubeInstances: []) -> str:
"""Show the blog main page
"""
blogStr = ''
@ -654,7 +661,7 @@ def htmlBlogView(authorized: bool,
return htmlBlogPage(authorized, session,
baseDir, httpPrefix, translate,
nickname, domain, port,
noOfItems, 1)
noOfItems, 1, peertubeInstances)
domainFull = getFullDomain(domain, port)

View File

@ -8,6 +8,7 @@ __status__ = "Production"
import os
from pprint import pprint
from utils import hasUsersPath
from utils import getFullDomain
from utils import removeIdEnding
from utils import removePostFromCache
@ -255,10 +256,7 @@ def bookmark(recentPostsCache: {},
bookmarkedPostNickname = getNicknameFromActor(acBm)
bookmarkedPostDomain, bookmarkedPostPort = getDomainFromActor(acBm)
else:
if '/users/' in objectUrl or \
'/accounts/' in objectUrl or \
'/channel/' in objectUrl or \
'/profile/' in objectUrl:
if hasUsersPath(objectUrl):
ou = objectUrl
bookmarkedPostNickname = getNicknameFromActor(ou)
bookmarkedPostDomain, bookmarkedPostPort = getDomainFromActor(ou)
@ -322,10 +320,7 @@ def undoBookmark(recentPostsCache: {},
bookmarkedPostNickname = getNicknameFromActor(acBm)
bookmarkedPostDomain, bookmarkedPostPort = getDomainFromActor(acBm)
else:
if '/users/' in objectUrl or \
'/accounts/' in objectUrl or \
'/channel/' in objectUrl or \
'/profile/' in objectUrl:
if hasUsersPath(objectUrl):
ou = objectUrl
bookmarkedPostNickname = getNicknameFromActor(ou)
bookmarkedPostDomain, bookmarkedPostPort = getDomainFromActor(ou)

103
briar.py 100644
View File

@ -0,0 +1,103 @@
__filename__ = "briar.py"
__author__ = "Bob Mottram"
__license__ = "AGPL3+"
__version__ = "1.1.0"
__maintainer__ = "Bob Mottram"
__email__ = "bob@freedombone.net"
__status__ = "Production"
def getBriarAddress(actorJson: {}) -> str:
"""Returns briar address for the given actor
"""
if not actorJson.get('attachment'):
return ''
for propertyValue in actorJson['attachment']:
if not propertyValue.get('name'):
continue
if not propertyValue['name'].lower().startswith('briar'):
continue
if not propertyValue.get('type'):
continue
if not propertyValue.get('value'):
continue
if propertyValue['type'] != 'PropertyValue':
continue
propertyValue['value'] = propertyValue['value'].strip()
if len(propertyValue['value']) < 50:
continue
if not propertyValue['value'].startswith('briar://'):
continue
if propertyValue['value'].lower() != propertyValue['value']:
continue
if '"' in propertyValue['value']:
continue
if ' ' in propertyValue['value']:
continue
if ',' in propertyValue['value']:
continue
if '.' in propertyValue['value']:
continue
return propertyValue['value']
return ''
def setBriarAddress(actorJson: {}, briarAddress: str) -> None:
"""Sets an briar address for the given actor
"""
notBriarAddress = False
if len(briarAddress) < 50:
notBriarAddress = True
if not briarAddress.startswith('briar://'):
notBriarAddress = True
if briarAddress.lower() != briarAddress:
notBriarAddress = True
if '"' in briarAddress:
notBriarAddress = True
if ' ' in briarAddress:
notBriarAddress = True
if '.' in briarAddress:
notBriarAddress = True
if ',' in briarAddress:
notBriarAddress = True
if '<' in briarAddress:
notBriarAddress = True
if not actorJson.get('attachment'):
actorJson['attachment'] = []
# remove any existing value
propertyFound = None
for propertyValue in actorJson['attachment']:
if not propertyValue.get('name'):
continue
if not propertyValue.get('type'):
continue
if not propertyValue['name'].lower().startswith('briar'):
continue
propertyFound = propertyValue
break
if propertyFound:
actorJson['attachment'].remove(propertyFound)
if notBriarAddress:
return
for propertyValue in actorJson['attachment']:
if not propertyValue.get('name'):
continue
if not propertyValue.get('type'):
continue
if not propertyValue['name'].lower().startswith('briar'):
continue
if propertyValue['type'] != 'PropertyValue':
continue
propertyValue['value'] = briarAddress
return
newBriarAddress = {
"name": "Briar",
"type": "PropertyValue",
"value": briarAddress
}
actorJson['attachment'].append(newBriarAddress)

690
daemon.py

File diff suppressed because it is too large Load Diff

View File

@ -8,6 +8,7 @@ __status__ = "Production"
import os
from datetime import datetime
from utils import hasUsersPath
from utils import getFullDomain
from utils import removeIdEnding
from utils import getNicknameFromActor
@ -139,10 +140,7 @@ def outboxDelete(baseDir: str, httpPrefix: str,
if debug:
print('DEBUG: c2s delete object is not a status')
return
if '/users/' not in messageId and \
'/accounts/' not in messageId and \
'/channel/' not in messageId and \
'/profile/' not in messageId:
if not hasUsersPath(messageId):
if debug:
print('DEBUG: c2s delete object has no nickname')
return

View File

@ -47,6 +47,7 @@ from tests import testClientToServer
from tests import runAllTests
from auth import storeBasicCredentials
from auth import createPassword
from utils import hasUsersPath
from utils import getFullDomain
from utils import setConfigParam
from utils import getConfigParam
@ -1318,10 +1319,7 @@ if args.actor:
for prefix in prefixes:
args.actor = args.actor.replace(prefix, '')
args.actor = args.actor.replace('/@', '/users/')
if '/users/' not in args.actor and \
'/accounts/' not in args.actor and \
'/channel/' not in args.actor and \
'/profile/' not in args.actor:
if not hasUsersPath(args.actor):
print('Expected actor format: ' +
'https://domain/@nick or https://domain/users/nick')
sys.exit()
@ -1391,10 +1389,7 @@ if args.actor:
personUrl = None
if wfRequest.get('errors'):
print('wfRequest error: ' + str(wfRequest['errors']))
if '/users/' in args.actor or \
'/accounts/' in args.actor or \
'/profile/' in args.actor or \
'/channel/' in args.actor:
if hasUsersPath(args.actor):
personUrl = originalActor
else:
sys.exit()

View File

@ -8,6 +8,7 @@ __status__ = "Production"
from pprint import pprint
import os
from utils import hasUsersPath
from utils import getFullDomain
from utils import isSystemAccount
from utils import getFollowersList
@ -316,10 +317,7 @@ def _getNoOfFollows(baseDir: str, nickname: str, domain: str,
ctr += 1
elif ((line.startswith('http') or
line.startswith('dat')) and
('/users/' in line or
'/profile/' in line or
'/accounts/' in line or
'/channel/' in line)):
hasUsersPath(line)):
ctr += 1
return ctr
@ -438,10 +436,7 @@ def getFollowingFeed(baseDir: str, domain: str, port: int, path: str,
following['orderedItems'].append(url)
elif ((line.startswith('http') or
line.startswith('dat')) and
('/users/' in line or
'/profile/' in line or
'/accounts/' in line or
'/channel/' in line)):
hasUsersPath(line)):
# https://domain/users/nickname
pageCtr += 1
totalCtr += 1
@ -616,10 +611,7 @@ def receiveFollowRequest(session, baseDir: str, httpPrefix: str,
if debug:
print('DEBUG: follow request has no actor')
return False
if '/users/' not in messageJson['actor'] and \
'/accounts/' not in messageJson['actor'] and \
'/channel/' not in messageJson['actor'] and \
'/profile/' not in messageJson['actor']:
if not hasUsersPath(messageJson['actor']):
if debug:
print('DEBUG: users/profile/accounts/channel missing from actor')
return False
@ -641,10 +633,7 @@ def receiveFollowRequest(session, baseDir: str, httpPrefix: str,
'nickname. Assuming single user instance.')
if not messageJson.get('to'):
messageJson['to'] = messageJson['object']
if '/users/' not in messageJson['object'] and \
'/accounts/' not in messageJson['object'] and \
'/channel/' not in messageJson['object'] and \
'/profile/' not in messageJson['object']:
if not hasUsersPath(messageJson['object']):
if debug:
print('DEBUG: users/profile/channel/accounts ' +
'not found within object')

View File

@ -10,6 +10,7 @@ import json
import os
import datetime
import time
from utils import hasUsersPath
from utils import validPostDate
from utils import getFullDomain
from utils import isEventPost
@ -145,7 +146,8 @@ def _inboxStorePostToHtmlCache(recentPostsCache: {}, maxRecentPosts: int,
nickname: str, domain: str, port: int,
postJsonObject: {},
allowDeletion: bool, boxname: str,
showPublishedDateOnly: bool) -> None:
showPublishedDateOnly: bool,
peertubeInstances: []) -> None:
"""Converts the json post into html and stores it in a cache
This enables the post to be quickly displayed later
"""
@ -170,6 +172,7 @@ def _inboxStorePostToHtmlCache(recentPostsCache: {}, maxRecentPosts: int,
avatarUrl, True, allowDeletion,
httpPrefix, __version__, boxname, None,
showPublishedDateOnly,
peertubeInstances,
not isDM(postJsonObject),
True, True, False, True)
@ -604,10 +607,7 @@ def _receiveUndoFollow(session, baseDir: str, httpPrefix: str,
if debug:
print('DEBUG: follow request has no actor within object')
return False
if '/users/' not in messageJson['object']['actor'] and \
'/accounts/' not in messageJson['object']['actor'] and \
'/channel/' not in messageJson['object']['actor'] and \
'/profile/' not in messageJson['object']['actor']:
if not hasUsersPath(messageJson['object']['actor']):
if debug:
print('DEBUG: "users" or "profile" missing ' +
'from actor within object')
@ -668,10 +668,7 @@ def _receiveUndo(session, baseDir: str, httpPrefix: str,
if debug:
print('DEBUG: follow request has no actor')
return False
if '/users/' not in messageJson['actor'] and \
'/accounts/' not in messageJson['actor'] and \
'/channel/' not in messageJson['actor'] and \
'/profile/' not in messageJson['actor']:
if not hasUsersPath(messageJson['actor']):
if debug:
print('DEBUG: "users" or "profile" missing from actor')
return False
@ -735,19 +732,19 @@ def _personReceiveUpdate(baseDir: str,
' ' + str(personJson))
domainFull = getFullDomain(domain, port)
updateDomainFull = getFullDomain(updateDomain, updatePort)
actor = updateDomainFull + '/users/' + updateNickname
if actor not in personJson['id']:
actor = updateDomainFull + '/profile/' + updateNickname
if actor not in personJson['id']:
actor = updateDomainFull + '/channel/' + updateNickname
if actor not in personJson['id']:
actor = updateDomainFull + '/accounts/' + updateNickname
if actor not in personJson['id']:
if debug:
print('actor: ' + actor)
print('id: ' + personJson['id'])
print('DEBUG: Actor does not match id')
return False
usersPaths = ('users', 'profile', 'channel', 'accounts')
usersStrFound = False
for usersStr in usersPaths:
actor = updateDomainFull + '/' + usersStr + '/' + updateNickname
if actor in personJson['id']:
usersStrFound = True
break
if not usersStrFound:
if debug:
print('actor: ' + actor)
print('id: ' + personJson['id'])
print('DEBUG: Actor does not match id')
return False
if updateDomainFull == domainFull:
if debug:
print('DEBUG: You can only receive actor updates ' +
@ -859,10 +856,7 @@ def _receiveUpdate(recentPostsCache: {}, session, baseDir: str,
if debug:
print('DEBUG: ' + messageJson['type'] + ' object has no type')
return False
if '/users/' not in messageJson['actor'] and \
'/accounts/' not in messageJson['actor'] and \
'/channel/' not in messageJson['actor'] and \
'/profile/' not in messageJson['actor']:
if not hasUsersPath(messageJson['actor']):
if debug:
print('DEBUG: "users" or "profile" missing from actor in ' +
messageJson['type'])
@ -943,10 +937,7 @@ def _receiveLike(recentPostsCache: {},
if debug:
print('DEBUG: ' + messageJson['type'] + ' has no "to" list')
return False
if '/users/' not in messageJson['actor'] and \
'/accounts/' not in messageJson['actor'] and \
'/channel/' not in messageJson['actor'] and \
'/profile/' not in messageJson['actor']:
if not hasUsersPath(messageJson['actor']):
if debug:
print('DEBUG: "users" or "profile" missing from actor in ' +
messageJson['type'])
@ -1014,10 +1005,7 @@ def _receiveUndoLike(recentPostsCache: {},
print('DEBUG: ' + messageJson['type'] +
' like object is not a string')
return False
if '/users/' not in messageJson['actor'] and \
'/accounts/' not in messageJson['actor'] and \
'/channel/' not in messageJson['actor'] and \
'/profile/' not in messageJson['actor']:
if not hasUsersPath(messageJson['actor']):
if debug:
print('DEBUG: "users" or "profile" missing from actor in ' +
messageJson['type'] + ' like')
@ -1219,10 +1207,7 @@ def _receiveDelete(session, handle: str, isGroup: bool, baseDir: str,
if debug:
print('DEBUG: ' + messageJson['type'] + ' has no "to" list')
return False
if '/users/' not in messageJson['actor'] and \
'/accounts/' not in messageJson['actor'] and \
'/channel/' not in messageJson['actor'] and \
'/profile/' not in messageJson['actor']:
if not hasUsersPath(messageJson['actor']):
if debug:
print('DEBUG: ' +
'"users" or "profile" missing from actor in ' +
@ -1303,19 +1288,13 @@ def _receiveAnnounce(recentPostsCache: {},
if debug:
print('DEBUG: ' + messageJson['type'] + ' has no "to" list')
return False
if '/users/' not in messageJson['actor'] and \
'/accounts/' not in messageJson['actor'] and \
'/channel/' not in messageJson['actor'] and \
'/profile/' not in messageJson['actor']:
if not hasUsersPath(messageJson['actor']):
if debug:
print('DEBUG: ' +
'"users" or "profile" missing from actor in ' +
messageJson['type'])
return False
if '/users/' not in messageJson['object'] and \
'/accounts/' not in messageJson['object'] and \
'/channel/' not in messageJson['object'] and \
'/profile/' not in messageJson['object']:
if not hasUsersPath(messageJson['object']):
if debug:
print('DEBUG: ' +
'"users", "channel" or "profile" missing in ' +
@ -1387,10 +1366,7 @@ def _receiveAnnounce(recentPostsCache: {},
if isinstance(attrib, str):
lookupActor = attrib
if lookupActor:
if '/users/' in lookupActor or \
'/accounts/' in lookupActor or \
'/channel/' in lookupActor or \
'/profile/' in lookupActor:
if hasUsersPath(lookupActor):
if '/statuses/' in lookupActor:
lookupActor = lookupActor.split('/statuses/')[0]
@ -1439,10 +1415,7 @@ def _receiveUndoAnnounce(recentPostsCache: {},
return False
if messageJson['object']['type'] != 'Announce':
return False
if '/users/' not in messageJson['actor'] and \
'/accounts/' not in messageJson['actor'] and \
'/channel/' not in messageJson['actor'] and \
'/profile/' not in messageJson['actor']:
if not hasUsersPath(messageJson['actor']):
if debug:
print('DEBUG: "users" or "profile" missing from actor in ' +
messageJson['type'] + ' announce')
@ -1688,10 +1661,7 @@ def _obtainAvatarForReplyPost(session, baseDir: str, httpPrefix: str,
if not isinstance(lookupActor, str):
return
if not ('/users/' in lookupActor or
'/accounts/' in lookupActor or
'/channel/' in lookupActor or
'/profile/' in lookupActor):
if not hasUsersPath(lookupActor):
return
if '/statuses/' in lookupActor:
@ -2071,7 +2041,8 @@ def _inboxAfterInitial(recentPostsCache: {}, maxRecentPosts: int,
maxMentions: int, maxEmoji: int, translate: {},
unitTest: bool, YTReplacementDomain: str,
showPublishedDateOnly: bool,
allowLocalNetworkAccess: bool) -> bool:
allowLocalNetworkAccess: bool,
peertubeInstances: []) -> bool:
""" Anything which needs to be done after initial checks have passed
"""
actor = keyId
@ -2378,7 +2349,8 @@ def _inboxAfterInitial(recentPostsCache: {}, maxRecentPosts: int,
postJsonObject,
allowDeletion,
boxname,
showPublishedDateOnly)
showPublishedDateOnly,
peertubeInstances)
if debug:
timeDiff = \
str(int((time.time() - htmlCacheStartTime) *
@ -2478,7 +2450,8 @@ def runInboxQueue(recentPostsCache: {}, maxRecentPosts: int,
YTReplacementDomain: str,
showPublishedDateOnly: bool,
allowNewsFollowers: bool,
maxFollowers: int, allowLocalNetworkAccess: bool) -> None:
maxFollowers: int, allowLocalNetworkAccess: bool,
peertubeInstances: []) -> None:
"""Processes received items and moves them to the appropriate
directories
"""
@ -2895,7 +2868,8 @@ def runInboxQueue(recentPostsCache: {}, maxRecentPosts: int,
translate, unitTest,
YTReplacementDomain,
showPublishedDateOnly,
allowLocalNetworkAccess)
allowLocalNetworkAccess,
peertubeInstances)
if debug:
pprint(queueJson['post'])

View File

@ -6,6 +6,7 @@ __maintainer__ = "Bob Mottram"
__email__ = "bob@freedombone.net"
__status__ = "Production"
from utils import hasUsersPath
from utils import getFullDomain
from utils import removeIdEnding
from utils import urlPermitted
@ -87,10 +88,7 @@ def _like(recentPostsCache: {},
likedPostNickname = getNicknameFromActor(actorLiked)
likedPostDomain, likedPostPort = getDomainFromActor(actorLiked)
else:
if '/users/' in objectUrl or \
'/accounts/' in objectUrl or \
'/channel/' in objectUrl or \
'/profile/' in objectUrl:
if hasUsersPath(objectUrl):
likedPostNickname = getNicknameFromActor(objectUrl)
likedPostDomain, likedPostPort = getDomainFromActor(objectUrl)

View File

@ -30,6 +30,7 @@ from session import postJsonString
from session import postImage
from webfinger import webfingerHandle
from httpsig import createSignedHeader
from utils import hasUsersPath
from utils import validPostDate
from utils import getFullDomain
from utils import getFollowersList
@ -155,10 +156,7 @@ def getUserUrl(wfRequest: {}, sourceId=0) -> str:
continue
if link['type'] != 'application/activity+json':
continue
if not ('/users/' in link['href'] or
'/accounts/' in link['href'] or
'/profile/' in link['href'] or
'/channel/' in link['href']):
if not hasUsersPath(link['href']):
print('getUserUrl webfinger activity+json ' +
'contains single user instance actor ' +
str(sourceId) + ' ' + str(link))

154
tests.py
View File

@ -854,7 +854,7 @@ def testFollowBetweenServers():
True, __version__, False)
print('sendResult: ' + str(sendResult))
for t in range(10):
for t in range(16):
if os.path.isfile(bobDir + '/accounts/bob@' +
bobDomain + '/followers.txt'):
if os.path.isfile(aliceDir + '/accounts/alice@' +
@ -2613,9 +2613,10 @@ def getFunctionCalls(name: str, lines: [], startLineCtr: int,
callsFunctions = []
functionContentStr = ''
for lineCtr in range(startLineCtr + 1, len(lines)):
if lines[lineCtr].startswith('def '):
lineStr = lines[lineCtr].strip()
if lineStr.startswith('def '):
break
if lines[lineCtr].startswith('class '):
if lineStr.startswith('class '):
break
functionContentStr += lines[lineCtr]
for funcName, properties in functionProperties.items():
@ -2635,14 +2636,14 @@ def functionArgsMatch(callArgs: [], funcArgs: []):
for a in callArgs:
if a == 'self':
continue
if '=' not in a:
if '=' not in a or a.startswith("'"):
callArgsCtr += 1
funcArgsCtr = 0
for a in funcArgs:
if a == 'self':
continue
if '=' not in a:
if '=' not in a or a.startswith("'"):
funcArgsCtr += 1
return callArgsCtr >= funcArgsCtr
@ -2670,7 +2671,7 @@ def testFunctions():
lines = f.readlines()
modules[modName]['lines'] = lines
for line in lines:
if not line.startswith('def '):
if not line.strip().startswith('def '):
continue
methodName = line.split('def ', 1)[1].split('(')[0]
methodArgs = \
@ -2694,7 +2695,9 @@ def testFunctions():
'pyjsonld'
]
excludeFuncs = [
'link'
'link',
'set',
'get'
]
# which modules is each function used within?
for modName, modProperties in modules.items():
@ -2702,7 +2705,11 @@ def testFunctions():
for name, properties in functionProperties.items():
lineCtr = 0
for line in modules[modName]['lines']:
if line.startswith('def '):
lineStr = line.strip()
if lineStr.startswith('def '):
lineCtr += 1
continue
if lineStr.startswith('class '):
lineCtr += 1
continue
if name + '(' in line:
@ -2735,7 +2742,22 @@ def testFunctions():
# don't check these functions, because they are procedurally called
exclusions = [
'do_GET',
'do_POST',
'do_HEAD',
'__run',
'globaltrace',
'localtrace',
'kill',
'clone',
'unregister_rdf_parser',
'set_document_loader',
'has_property',
'has_value',
'add_value',
'get_values',
'remove_property',
'remove_value',
'normalize',
'get_document_loader',
'runInboxQueueWatchdog',
@ -2764,17 +2786,26 @@ def testFunctions():
'setOrganizationScheme'
]
excludeImports = [
'link'
'link',
'start'
]
excludeLocal = [
'pyjsonld',
'daemon',
'tests'
]
excludeMods = [
'pyjsonld'
]
# check that functions are called somewhere
for name, properties in functionProperties.items():
if name.startswith('__'):
if name.endswith('__'):
continue
if name in exclusions:
continue
if properties['module'] in excludeMods:
continue
isLocalFunction = False
if not properties['calledInModule']:
print('function ' + name +
@ -2815,18 +2846,80 @@ def testFunctions():
assert False
print('Function: ' + name + '')
print('Constructing call graph')
print('Constructing function call graph')
moduleColors = ('red', 'green', 'yellow', 'orange', 'purple', 'cyan',
'darkgoldenrod3', 'darkolivegreen1', 'darkorange1',
'darkorchid1', 'darkseagreen', 'darkslategray4',
'deeppink1', 'deepskyblue1', 'dimgrey', 'gold1',
'goldenrod', 'burlywood2', 'bisque1', 'brown1',
'chartreuse2', 'cornsilk', 'darksalmon')
maxModuleCalls = 1
maxFunctionCalls = 1
colorCtr = 0
for modName, modProperties in modules.items():
lineCtr = 0
modules[modName]['color'] = moduleColors[colorCtr]
colorCtr += 1
if colorCtr >= len(moduleColors):
colorCtr = 0
for line in modules[modName]['lines']:
if line.startswith('def '):
if line.strip().startswith('def '):
name = line.split('def ')[1].split('(')[0]
callsList = \
getFunctionCalls(name, modules[modName]['lines'],
lineCtr, functionProperties)
functionProperties[name]['calls'] = callsList.copy()
if len(callsList) > maxFunctionCalls:
maxFunctionCalls = len(callsList)
# keep track of which module calls which other module
for fn in callsList:
modCall = functionProperties[fn]['module']
if modCall != modName:
if modules[modName].get('calls'):
if modCall not in modules[modName]['calls']:
modules[modName]['calls'].append(modCall)
if len(modules[modName]['calls']) > \
maxModuleCalls:
maxModuleCalls = \
len(modules[modName]['calls'])
else:
modules[modName]['calls'] = [modCall]
lineCtr += 1
callGraphStr = 'digraph EpicyonModules {\n\n'
callGraphStr += ' graph [fontsize=10 fontname="Verdana" compound=true];\n'
callGraphStr += ' node [shape=record fontsize=10 fontname="Verdana"];\n\n'
# colors of modules nodes
for modName, modProperties in modules.items():
if not modProperties.get('calls'):
callGraphStr += ' "' + modName + \
'" [fillcolor=yellow style=filled];\n'
continue
if len(modProperties['calls']) <= int(maxModuleCalls / 8):
callGraphStr += ' "' + modName + \
'" [fillcolor=green style=filled];\n'
elif len(modProperties['calls']) < int(maxModuleCalls / 4):
callGraphStr += ' "' + modName + \
'" [fillcolor=orange style=filled];\n'
else:
callGraphStr += ' "' + modName + \
'" [fillcolor=red style=filled];\n'
callGraphStr += '\n'
# connections between modules
for modName, modProperties in modules.items():
if not modProperties.get('calls'):
continue
for modCall in modProperties['calls']:
callGraphStr += ' "' + modName + '" -> "' + modCall + '";\n'
callGraphStr += '\n}\n'
with open('epicyon_modules.dot', 'w+') as fp:
fp.write(callGraphStr)
print('Modules call graph saved to epicyon_modules.dot')
print('Plot using: ' +
'sfdp -x -Goverlap=false -Goverlap_scaling=2 ' +
'-Gsep=+100 -Tx11 epicyon_modules.dot')
callGraphStr = 'digraph Epicyon {\n\n'
callGraphStr += ' size="8,6"; ratio=fill;\n'
callGraphStr += ' graph [fontsize=10 fontname="Verdana" compound=true];\n'
callGraphStr += ' node [shape=record fontsize=10 fontname="Verdana"];\n\n'
@ -2834,25 +2927,52 @@ def testFunctions():
callGraphStr += ' subgraph cluster_' + modName + ' {\n'
callGraphStr += ' label = "' + modName + '";\n'
callGraphStr += ' node [style=filled];\n'
callGraphStr += ' '
moduleFunctionsStr = ''
for name in modProperties['functions']:
callGraphStr += '"' + name + '" '
callGraphStr += ';\n'
if name.startswith('test'):
continue
if name not in excludeFuncs:
if not functionProperties[name]['calls']:
moduleFunctionsStr += \
' "' + name + '" [fillcolor=yellow style=filled];\n'
continue
noOfCalls = len(functionProperties[name]['calls'])
if noOfCalls < int(maxFunctionCalls / 4):
moduleFunctionsStr += ' "' + name + \
'" [fillcolor=orange style=filled];\n'
else:
moduleFunctionsStr += ' "' + name + \
'" [fillcolor=red style=filled];\n'
if moduleFunctionsStr:
callGraphStr += moduleFunctionsStr + '\n'
callGraphStr += ' color=blue;\n'
callGraphStr += ' }\n\n'
for name, properties in functionProperties.items():
if not properties['calls']:
continue
noOfCalls = len(properties['calls'])
if noOfCalls <= int(maxFunctionCalls / 8):
modColor = 'blue'
elif noOfCalls < int(maxFunctionCalls / 4):
modColor = 'green'
else:
modColor = 'red'
for calledFunc in properties['calls']:
callGraphStr += ' "' + name + '" -> "' + calledFunc + '";\n'
if calledFunc.startswith('test'):
continue
if calledFunc not in excludeFuncs:
callGraphStr += ' "' + name + '" -> "' + calledFunc + \
'" [color=' + modColor + '];\n'
callGraphStr += '\n}\n'
with open('epicyon.dot', 'w+') as fp:
fp.write(callGraphStr)
print('Call graph saved to epicyon.dot')
print('Convert to image with: ' +
'dot -Tjpg epicyon.dot -o epicyon_diagram.jpg')
print('Plot using: ' +
'sfdp -x -Goverlap=prism -Goverlap_scaling=8 ' +
'-Gsep=+120 -Tx11 epicyon.dot')
def runAllTests():

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1,4 +1,6 @@
{
"post-separator-margin-top": "10px",
"post-separator-margin-bottom": "10px",
"calendar-header-font-style": "normal",
"italic-font-style": "normal",
"calendar-header-font": "'Orbitron'",
@ -22,8 +24,6 @@
"publish-button-at-top": "False",
"main-visited-color": "#46eed5",
"options-main-visited-color": "#46eed5",
"post-separator-margin-top": "9%",
"post-separator-margin-bottom": "9%",
"post-separator-width": "80%",
"post-separator-height": "10%",
"column-left-header-background": "#6800e7",

View File

@ -347,5 +347,7 @@
"Filter out words": "تصفية الكلمات",
"Unfilter": "غير مرشح",
"Unfilter words": "الكلمات غير المصفاة",
"Show Accounts": "إظهار الحسابات"
"Show Accounts": "إظهار الحسابات",
"Peertube Instances": "مثيلات Peertube",
"Show video previews for the following Peertube sites.": "إظهار معاينات الفيديو لمواقع Peertube التالية."
}

View File

@ -347,5 +347,7 @@
"Filter out words": "Filtra les paraules",
"Unfilter": "Sense filtre",
"Unfilter words": "Paraules sense filtre",
"Show Accounts": "Mostra comptes"
"Show Accounts": "Mostra comptes",
"Peertube Instances": "Instàncies de Peertube",
"Show video previews for the following Peertube sites.": "Mostra les previsualitzacions de vídeo dels següents llocs de Peertube."
}

View File

@ -347,5 +347,7 @@
"Filter out words": "Hidlo geiriau",
"Unfilter": "Di-hid",
"Unfilter words": "Geiriau di-hid",
"Show Accounts": "Dangos Cyfrifon"
"Show Accounts": "Dangos Cyfrifon",
"Peertube Instances": "Camau Peertube",
"Show video previews for the following Peertube sites.": "Dangos rhagolygon fideo ar gyfer y safleoedd Peertube canlynol."
}

View File

@ -347,5 +347,7 @@
"Filter out words": "Wörter herausfiltern",
"Unfilter": "Filter entfernen",
"Unfilter words": "Wörter herausfiltern",
"Show Accounts": "Konten anzeigen"
"Show Accounts": "Konten anzeigen",
"Peertube Instances": "Peertube-Instanzen",
"Show video previews for the following Peertube sites.": "Zeigen Sie eine Videovorschau für die folgenden Peertube-Websites an."
}

View File

@ -347,5 +347,7 @@
"Filter out words": "Filter out words",
"Unfilter": "Unfilter",
"Unfilter words": "Unfilter words",
"Show Accounts": "Show Accounts"
"Show Accounts": "Show Accounts",
"Peertube Instances": "Peertube Instances",
"Show video previews for the following Peertube sites.": "Show video previews for the following Peertube sites."
}

View File

@ -347,5 +347,7 @@
"Filter out words": "Filtrar palabras",
"Unfilter": "Unfilter",
"Unfilter words": "Palabras sin filtrar",
"Show Accounts": "Mostrar cuentas"
"Show Accounts": "Mostrar cuentas",
"Peertube Instances": "Instancias de Peertube",
"Show video previews for the following Peertube sites.": "Muestre vistas previas de video para los siguientes sitios de Peertube."
}

View File

@ -347,5 +347,7 @@
"Filter out words": "Filtrer les mots",
"Unfilter": "Non filtrer",
"Unfilter words": "Mots non filtrés",
"Show Accounts": "Afficher les comptes"
"Show Accounts": "Afficher les comptes",
"Peertube Instances": "Instances Peertube",
"Show video previews for the following Peertube sites.": "Afficher des aperçus vidéo pour les sites Peertube suivants."
}

View File

@ -347,5 +347,7 @@
"Filter out words": "Scag focail amach",
"Unfilter": "Neamhleithleach",
"Unfilter words": "Focail neamhleithleacha",
"Show Accounts": "Taispeáin Cuntais"
"Show Accounts": "Taispeáin Cuntais",
"Peertube Instances": "Imeachtaí Peertube",
"Show video previews for the following Peertube sites.": "Taispeáin réamhamharcanna físe do na suíomhanna Peertube seo a leanas."
}

View File

@ -347,5 +347,7 @@
"Filter out words": "शब्दों को फ़िल्टर करें",
"Unfilter": "Unfilter",
"Unfilter words": "अनफ़िल्टर शब्द",
"Show Accounts": "खाते दिखाएं"
"Show Accounts": "खाते दिखाएं",
"Peertube Instances": "Peertube उदाहरण",
"Show video previews for the following Peertube sites.": "निम्नलिखित Peertube साइटों के लिए वीडियो पूर्वावलोकन दिखाएं।"
}

View File

@ -347,5 +347,7 @@
"Filter out words": "Filtra le parole",
"Unfilter": "Unfilter",
"Unfilter words": "Parole non filtrate",
"Show Accounts": "Mostra account"
"Show Accounts": "Mostra account",
"Peertube Instances": "Istanze di Peertube",
"Show video previews for the following Peertube sites.": "Mostra le anteprime dei video per i seguenti siti Peertube."
}

View File

@ -347,5 +347,7 @@
"Filter out words": "単語を除外する",
"Unfilter": "フィルタリング解除",
"Unfilter words": "単語のフィルタリングを解除する",
"Show Accounts": "アカウントを表示する"
"Show Accounts": "アカウントを表示する",
"Peertube Instances": "Peertubeインスタンス",
"Show video previews for the following Peertube sites.": "次のPeertubeサイトのビデオプレビューを表示します。"
}

View File

@ -343,5 +343,7 @@
"Filter out words": "Filter out words",
"Unfilter": "Unfilter",
"Unfilter words": "Unfilter words",
"Show Accounts": "Show Accounts"
"Show Accounts": "Show Accounts",
"Peertube Instances": "Peertube Instances",
"Show video previews for the following Peertube sites.": "Show video previews for the following Peertube sites."
}

View File

@ -347,5 +347,7 @@
"Filter out words": "Filtrar palavras",
"Unfilter": "Unfilter",
"Unfilter words": "Palavras sem filtro",
"Show Accounts": "Mostrar contas"
"Show Accounts": "Mostrar contas",
"Peertube Instances": "Instâncias Peertube",
"Show video previews for the following Peertube sites.": "Mostrar visualizações de vídeo para os seguintes sites Peertube."
}

View File

@ -347,5 +347,7 @@
"Filter out words": "Отфильтровать слова",
"Unfilter": "Нефильтровать",
"Unfilter words": "Не фильтровать слова",
"Show Accounts": "Показать счета"
"Show Accounts": "Показать счета",
"Peertube Instances": "Экземпляры Peertube",
"Show video previews for the following Peertube sites.": "Показать превью видео для следующих сайтов Peertube."
}

View File

@ -347,5 +347,7 @@
"Filter out words": "过滤掉单词",
"Unfilter": "取消过滤",
"Unfilter words": "未过滤字词",
"Show Accounts": "显示帐户"
"Show Accounts": "显示帐户",
"Peertube Instances": "Peertube实例",
"Show video previews for the following Peertube sites.": "显示以下Peertube网站的视频预览。"
}

View File

@ -19,6 +19,16 @@ from calendar import monthrange
from followingCalendar import addPersonToCalendar
def hasUsersPath(pathStr: str) -> bool:
"""Whether there is a /users/ path (or equivalent) in the given string
"""
usersList = ('users', 'accounts', 'channel', 'profile')
for usersStr in usersList:
if '/' + usersStr + '/' in pathStr:
return True
return False
def validPostDate(published: str, maxAgeDays=7) -> bool:
"""Returns true if the published date is recent and is not in the future
"""

View File

@ -28,7 +28,8 @@ def htmlConfirmDelete(cssCache: {},
wfRequest: {}, personCache: {},
callingDomain: str,
YTReplacementDomain: str,
showPublishedDateOnly: bool) -> str:
showPublishedDateOnly: bool,
peertubeInstances: []) -> str:
"""Shows a screen asking to confirm the deletion of a post
"""
if '/statuses/' not in messageId:
@ -66,6 +67,7 @@ def htmlConfirmDelete(cssCache: {},
httpPrefix, projectVersion, 'outbox',
YTReplacementDomain,
showPublishedDateOnly,
peertubeInstances,
False, False, False, False, False)
deletePostStr += '<center>'
deletePostStr += \

View File

@ -27,7 +27,8 @@ def _htmlFrontScreenPosts(recentPostsCache: {}, maxRecentPosts: int,
session, wfRequest: {}, personCache: {},
projectVersion: str,
YTReplacementDomain: str,
showPublishedDateOnly: bool) -> str:
showPublishedDateOnly: bool,
peertubeInstances: []) -> str:
"""Shows posts on the front screen of a news instance
These should only be public blog posts from the features timeline
which is the blog timeline of the news actor
@ -65,6 +66,7 @@ def _htmlFrontScreenPosts(recentPostsCache: {}, maxRecentPosts: int,
httpPrefix, projectVersion, 'inbox',
YTReplacementDomain,
showPublishedDateOnly,
peertubeInstances,
False, False, False, True, False)
if postStr:
profileStr += postStr + separatorStr
@ -85,7 +87,9 @@ def htmlFrontScreen(rssIconAtTop: bool,
session, wfRequest: {}, personCache: {},
YTReplacementDomain: str,
showPublishedDateOnly: bool,
newswire: {}, theme: str, extraJson=None,
newswire: {}, theme: str,
peertubeInstances: [],
extraJson=None,
pageNumber=None, maxItemsPerPage=None) -> str:
"""Show the news instance front screen
"""
@ -148,7 +152,8 @@ def htmlFrontScreen(rssIconAtTop: bool,
session, wfRequest, personCache,
projectVersion,
YTReplacementDomain,
showPublishedDateOnly) + licenseStr
showPublishedDateOnly,
peertubeInstances) + licenseStr
# Footer which is only used for system accounts
profileFooterStr = ' </td>\n'

View File

@ -6,8 +6,30 @@ __maintainer__ = "Bob Mottram"
__email__ = "bob@freedombone.net"
__status__ = "Production"
import os
def loadPeertubeInstances(baseDir: str, peertubeInstances: []) -> None:
"""Loads peertube instances from file into the given list
"""
peertubeList = None
peertubeInstancesFilename = baseDir + '/accounts/peertube.txt'
if os.path.isfile(peertubeInstancesFilename):
with open(peertubeInstancesFilename, 'r') as fp:
peertubeStr = fp.read()
if peertubeStr:
peertubeStr = peertubeStr.replace('\r', '')
peertubeList = peertubeStr.split('\n')
if not peertubeList:
return
for url in peertubeList:
if url in peertubeInstances:
continue
peertubeInstances.append(url)
def _addEmbeddedVideoFromSites(translate: {}, content: str,
peertubeInstances: [],
width=400, height=300) -> str:
"""Adds embedded videos
"""
@ -39,8 +61,14 @@ def _addEmbeddedVideoFromSites(translate: {}, content: str,
"allowfullscreen></iframe>\n</center>\n"
return content
invidiousSites = ('https://invidio.us',
'https://invidious.snopyta.org',
invidiousSites = ('https://invidious.snopyta.org',
'https://yewtu.be',
'https://tube.connect.cafe',
'https://invidious.kavin.rocks',
'https://invidiou.site',
'https://invidious.tube',
'https://invidious.xyz',
'https://invidious.zapashcanon.fr',
'http://c7hqkpkpemu6e7emz5b4vy' +
'z7idjgdvgaaa3dyimmeojqbgpea3xqjoid.onion',
'http://axqzx4s6s54s32yentfqojs3x5i7faxza6xo3ehd4' +
@ -76,38 +104,59 @@ def _addEmbeddedVideoFromSites(translate: {}, content: str,
return content
if '"https://' in content:
# A selection of the current larger peertube sites, mostly
# French and German language
# These have been chosen based on reported numbers of users
# and the content of each has not been reviewed, so mileage could vary
peerTubeSites = ('peertube.mastodon.host', 'open.tube', 'share.tube',
'tube.tr4sk.me', 'videos.elbinario.net',
'hkvideo.live',
'peertube.snargol.com', 'tube.22decembre.eu',
'tube.fabrigli.fr', 'libretube.net', 'libre.video',
'peertube.linuxrocks.online', 'spacepub.space',
'video.ploud.jp', 'video.omniatv.com',
'peertube.servebeer.com',
'tube.tchncs.de', 'tubee.fr', 'video.alternanet.fr',
'devtube.dev-wiki.de', 'video.samedi.pm',
'video.irem.univ-paris-diderot.fr',
'peertube.openstreetmap.fr', 'video.antopie.org',
'scitech.video', 'tube.4aem.com', 'video.ploud.fr',
'peervideo.net', 'video.valme.io',
'videos.pair2jeux.tube',
'vault.mle.party', 'hostyour.tv',
'diode.zone', 'visionon.tv',
'artitube.artifaille.fr', 'peertube.fr',
'peertube.live', 'kolektiva.media',
'tube.ac-lyon.fr', 'www.yiny.org', 'betamax.video',
'tube.piweb.be', 'pe.ertu.be', 'peertube.social',
'videos.lescommuns.org', 'peertube.nogafa.org',
'skeptikon.fr', 'video.tedomum.net',
'tube.p2p.legal', 'tilvids.com',
'sikke.fi', 'exode.me', 'peertube.video')
if peertubeInstances:
peerTubeSites = peertubeInstances
else:
# A default selection of the current larger peertube sites,
# mostly French and German language.
# These have only been semi-vetted, and so should be under
# continuous review.
# Also see https://peertube_isolation.frama.io/list/ for
# adversarial instances. Nothing in that list should be
# in the defaults below.
peerTubeSites = ('share.tube',
'tube.22decembre.eu',
'libre.video',
'peertube.linuxrocks.online',
'spacepub.space',
'tube.tchncs.de',
'video.irem.univ-paris-diderot.fr',
'peertube.openstreetmap.fr',
'video.antopie.org',
'scitech.video',
'video.ploud.fr',
'diode.zone',
'visionon.tv',
'peertube.fr',
'peertube.live',
'kolektiva.media',
'betamax.video',
'peertube.social',
'videos.lescommuns.org',
'video.tedomum.net',
'tilvids.com',
'exode.me',
'peertube.video')
for site in peerTubeSites:
if '"https://' + site in content:
url = content.split('"https://' + site)[1]
site = site.strip()
if not site:
continue
if len(site) < 5:
continue
if '.' not in site:
continue
siteStr = site
if site.startswith('http://'):
site = site.replace('http://', '')
elif site.startswith('https://'):
site = site.replace('https://', '')
if site.endswith('.onion') or site.endswith('.i2p'):
siteStr = 'http://' + site
else:
siteStr = 'https://' + site
siteStr = '"' + siteStr
if siteStr in content:
url = content.split(siteStr)[1]
if '"' in url:
url = url.split('"')[0].replace('/watch/', '/embed/')
content = \
@ -216,9 +265,11 @@ def _addEmbeddedVideo(translate: {}, content: str,
return content
def addEmbeddedElements(translate: {}, content: str) -> str:
def addEmbeddedElements(translate: {}, content: str,
peertubeInstances: []) -> str:
"""Adds embedded elements for various media types
"""
content = _addEmbeddedVideoFromSites(translate, content)
content = _addEmbeddedVideoFromSites(translate, content,
peertubeInstances)
content = _addEmbeddedAudio(translate, content)
return _addEmbeddedVideo(translate, content)

View File

@ -37,7 +37,7 @@ def htmlModeration(cssCache: {}, defaultTimeline: str,
rssIconAtTop: bool,
publishButtonAtTop: bool,
authorized: bool, moderationActionStr: str,
theme: str) -> str:
theme: str, peertubeInstances: []) -> str:
"""Show the moderation feed as html
This is what you see when selecting the "mod" timeline
"""
@ -51,7 +51,8 @@ def htmlModeration(cssCache: {}, defaultTimeline: str,
newswire, False, False, positiveVoting,
showPublishAsIcon, fullWidthTimelineButtonHeader,
iconsAsButtons, rssIconAtTop, publishButtonAtTop,
authorized, moderationActionStr, theme)
authorized, moderationActionStr, theme,
peertubeInstances)
def htmlAccountInfo(cssCache: {}, translate: {},

View File

@ -39,6 +39,7 @@ def htmlPersonOptions(defaultTimeline: str,
ssbAddress: str,
blogAddress: str,
toxAddress: str,
briarAddress: str,
jamiAddress: str,
PGPpubKey: str,
PGPfingerprint: str,
@ -141,6 +142,15 @@ def htmlPersonOptions(defaultTimeline: str,
if toxAddress:
optionsStr += \
'<p class="imText">Tox: ' + removeHtml(toxAddress) + '</p>\n'
if briarAddress:
if briarAddress.startswith('briar://'):
optionsStr += \
'<p class="imText">' + \
removeHtml(briarAddress) + '</p>\n'
else:
optionsStr += \
'<p class="imText">briar://' + \
removeHtml(briarAddress) + '</p>\n'
if jamiAddress:
optionsStr += \
'<p class="imText">Jami: ' + removeHtml(jamiAddress) + '</p>\n'

View File

@ -1076,6 +1076,7 @@ def individualPostAsHtml(allowDownloads: bool,
httpPrefix: str, projectVersion: str,
boxName: str, YTReplacementDomain: str,
showPublishedDateOnly: bool,
peertubeInstances: [],
showRepeats=True,
showIcons=False,
manuallyApprovesFollowers=False,
@ -1483,7 +1484,8 @@ def individualPostAsHtml(allowDownloads: bool,
if not postIsSensitive:
contentStr = objectContent + attachmentStr
contentStr = addEmbeddedElements(translate, contentStr)
contentStr = addEmbeddedElements(translate, contentStr,
peertubeInstances)
contentStr = insertQuestion(baseDir, translate,
nickname, domain, port,
contentStr, postJsonObject,
@ -1499,7 +1501,8 @@ def individualPostAsHtml(allowDownloads: bool,
# get the content warning text
cwContentStr = objectContent + attachmentStr
if not isPatch:
cwContentStr = addEmbeddedElements(translate, cwContentStr)
cwContentStr = addEmbeddedElements(translate, cwContentStr,
peertubeInstances)
cwContentStr = \
insertQuestion(baseDir, translate, nickname, domain, port,
cwContentStr, postJsonObject, pageNumber)
@ -1571,7 +1574,8 @@ def htmlIndividualPost(cssCache: {},
postJsonObject: {}, httpPrefix: str,
projectVersion: str, likedBy: str,
YTReplacementDomain: str,
showPublishedDateOnly: bool) -> str:
showPublishedDateOnly: bool,
peertubeInstances: []) -> str:
"""Show an individual post as html
"""
postStr = ''
@ -1611,6 +1615,7 @@ def htmlIndividualPost(cssCache: {},
httpPrefix, projectVersion, 'inbox',
YTReplacementDomain,
showPublishedDateOnly,
peertubeInstances,
False, authorized, False, False, False)
messageId = removeIdEnding(postJsonObject['id'])
@ -1636,6 +1641,7 @@ def htmlIndividualPost(cssCache: {},
httpPrefix, projectVersion, 'inbox',
YTReplacementDomain,
showPublishedDateOnly,
peertubeInstances,
False, authorized,
False, False, False) + postStr
@ -1664,6 +1670,7 @@ def htmlIndividualPost(cssCache: {},
httpPrefix, projectVersion, 'inbox',
YTReplacementDomain,
showPublishedDateOnly,
peertubeInstances,
False, authorized,
False, False, False)
cssFilename = baseDir + '/epicyon-profile.css'
@ -1680,7 +1687,8 @@ def htmlPostReplies(cssCache: {},
nickname: str, domain: str, port: int, repliesJson: {},
httpPrefix: str, projectVersion: str,
YTReplacementDomain: str,
showPublishedDateOnly: bool) -> str:
showPublishedDateOnly: bool,
peertubeInstances: []) -> str:
"""Show the replies to an individual post as html
"""
repliesStr = ''
@ -1696,6 +1704,7 @@ def htmlPostReplies(cssCache: {},
httpPrefix, projectVersion, 'inbox',
YTReplacementDomain,
showPublishedDateOnly,
peertubeInstances,
False, False, False, False, False)
cssFilename = baseDir + '/epicyon-profile.css'

View File

@ -8,6 +8,7 @@ __status__ = "Production"
import os
from pprint import pprint
from utils import hasUsersPath
from utils import getFullDomain
from utils import isDormant
from utils import getNicknameFromActor
@ -33,6 +34,7 @@ from pgp import getEmailAddress
from pgp import getPGPfingerprint
from pgp import getPGPpubKey
from tox import getToxAddress
from briar import getBriarAddress
from jami import getJamiAddress
from filters import isFiltered
from webapp_frontscreen import htmlFrontScreen
@ -58,14 +60,11 @@ def htmlProfileAfterSearch(cssCache: {},
debug: bool, projectVersion: str,
YTReplacementDomain: str,
showPublishedDateOnly: bool,
defaultTimeline: str) -> str:
defaultTimeline: str,
peertubeInstances: []) -> str:
"""Show a profile page after a search for a fediverse address
"""
if '/users/' in profileHandle or \
'/accounts/' in profileHandle or \
'/channel/' in profileHandle or \
'/profile/' in profileHandle or \
'/@' in profileHandle:
if hasUsersPath(profileHandle) or '/@' in profileHandle:
searchNickname = getNicknameFromActor(profileHandle)
searchDomain, searchPort = getDomainFromActor(profileHandle)
else:
@ -279,6 +278,7 @@ def htmlProfileAfterSearch(cssCache: {},
httpPrefix, projectVersion, 'inbox',
YTReplacementDomain,
showPublishedDateOnly,
peertubeInstances,
False, False, False, False, False)
i += 1
if i >= 20:
@ -372,6 +372,7 @@ def htmlProfile(rssIconAtTop: bool,
YTReplacementDomain: str,
showPublishedDateOnly: bool,
newswire: {}, theme: str, dormantMonths: int,
peertubeInstances: [],
extraJson=None, pageNumber=None,
maxItemsPerPage=None) -> str:
"""Show the profile page as html
@ -443,9 +444,11 @@ def htmlProfile(rssIconAtTop: bool,
matrixAddress = getMatrixAddress(profileJson)
ssbAddress = getSSBAddress(profileJson)
toxAddress = getToxAddress(profileJson)
briarAddress = getBriarAddress(profileJson)
jamiAddress = getJamiAddress(profileJson)
if donateUrl or xmppAddress or matrixAddress or \
ssbAddress or toxAddress or jamiAddress or PGPpubKey or \
ssbAddress or toxAddress or briarAddress or \
jamiAddress or PGPpubKey or \
PGPfingerprint or emailAddress:
donateSection = '<div class="container">\n'
donateSection += ' <center>\n'
@ -473,6 +476,15 @@ def htmlProfile(rssIconAtTop: bool,
donateSection += \
'<p>Tox: <label class="toxaddr">' + \
toxAddress + '</label></p>\n'
if briarAddress:
if briarAddress.startswith('briar://'):
donateSection += \
'<p><label class="toxaddr">' + \
briarAddress + '</label></p>\n'
else:
donateSection += \
'<p>briar://<label class="toxaddr">' + \
briarAddress + '</label></p>\n'
if jamiAddress:
donateSection += \
'<p>Jami: <label class="toxaddr">' + \
@ -628,7 +640,8 @@ def htmlProfile(rssIconAtTop: bool,
session, wfRequest, personCache,
projectVersion,
YTReplacementDomain,
showPublishedDateOnly) + licenseStr
showPublishedDateOnly,
peertubeInstances) + licenseStr
elif selected == 'following':
profileStr += \
_htmlProfileFollowing(translate, baseDir, httpPrefix,
@ -674,7 +687,8 @@ def _htmlProfilePosts(recentPostsCache: {}, maxRecentPosts: int,
session, wfRequest: {}, personCache: {},
projectVersion: str,
YTReplacementDomain: str,
showPublishedDateOnly: bool) -> str:
showPublishedDateOnly: bool,
peertubeInstances: []) -> str:
"""Shows posts on the profile screen
These should only be public posts
"""
@ -712,6 +726,7 @@ def _htmlProfilePosts(recentPostsCache: {}, maxRecentPosts: int,
httpPrefix, projectVersion, 'inbox',
YTReplacementDomain,
showPublishedDateOnly,
peertubeInstances,
False, False, False, True, False)
if postStr:
profileStr += postStr + separatorStr
@ -833,7 +848,8 @@ def _htmlProfileShares(actor: str, translate: {},
def htmlEditProfile(cssCache: {}, translate: {}, baseDir: str, path: str,
domain: str, port: int, httpPrefix: str,
defaultTimeline: str, theme: str) -> str:
defaultTimeline: str, theme: str,
peertubeInstances: []) -> str:
"""Shows the edit profile screen
"""
imageFormats = getImageFormats()
@ -873,6 +889,7 @@ def htmlEditProfile(cssCache: {}, translate: {}, baseDir: str, path: str,
ssbAddress = ''
blogAddress = ''
toxAddress = ''
briarAddress = ''
manuallyApprovesFollowers = ''
actorJson = loadJson(actorFilename)
if actorJson:
@ -882,6 +899,7 @@ def htmlEditProfile(cssCache: {}, translate: {}, baseDir: str, path: str,
ssbAddress = getSSBAddress(actorJson)
blogAddress = getBlogAddress(actorJson)
toxAddress = getToxAddress(actorJson)
briarAddress = getBriarAddress(actorJson)
jamiAddress = getJamiAddress(actorJson)
emailAddress = getEmailAddress(actorJson)
PGPpubKey = getPGPpubKey(actorJson)
@ -1029,6 +1047,7 @@ def htmlEditProfile(cssCache: {}, translate: {}, baseDir: str, path: str,
themesDropdown = ''
instanceStr = ''
editorsStr = ''
peertubeStr = ''
adminNickname = getConfigParam(baseDir, 'admin')
if adminNickname:
@ -1145,6 +1164,21 @@ def htmlEditProfile(cssCache: {}, translate: {}, baseDir: str, path: str,
'<option value="' + themeName +
'" selected>')
peertubeStr = \
' <br><b><label class="labels">' + \
translate['Peertube Instances'] + '</label></b>\n'
idx = 'Show video previews for the following Peertube sites.'
peertubeStr += \
' <br><label class="labels">' + \
translate[idx] + '</label>\n'
peertubeInstancesStr = ''
for url in peertubeInstances:
peertubeInstancesStr += url + '\n'
peertubeStr += \
' <textarea id="message" name="ptInstances" ' + \
'style="height:200px">' + peertubeInstancesStr + \
'</textarea>\n'
editProfileForm = htmlHeaderWithExternalStyle(cssFilename)
# top banner
@ -1220,6 +1254,11 @@ def htmlEditProfile(cssCache: {}, translate: {}, baseDir: str, path: str,
' <input type="text" name="toxAddress" value="' + \
toxAddress + '">\n'
editProfileForm += '<label class="labels">Briar</label><br>\n'
editProfileForm += \
' <input type="text" name="briarAddress" value="' + \
briarAddress + '">\n'
editProfileForm += '<label class="labels">Jami</label><br>\n'
editProfileForm += \
' <input type="text" name="jamiAddress" value="' + \
@ -1436,7 +1475,7 @@ def htmlEditProfile(cssCache: {}, translate: {}, baseDir: str, path: str,
editProfileForm += ' <label class="labels">' + \
translate[idx] + '</label>\n'
editProfileForm += skillsStr + themesDropdown
editProfileForm += moderatorsStr + editorsStr
editProfileForm += moderatorsStr + editorsStr + peertubeStr
editProfileForm += ' </div>\n' + instanceStr
editProfileForm += ' <div class="container">\n'
editProfileForm += ' <b><label class="labels">' + \

View File

@ -506,7 +506,8 @@ def htmlHistorySearch(cssCache: {}, translate: {}, baseDir: str,
personCache: {},
port: int,
YTReplacementDomain: str,
showPublishedDateOnly: bool) -> str:
showPublishedDateOnly: bool,
peertubeInstances: []) -> str:
"""Show a page containing search results for your post history
"""
if historysearch.startswith('!'):
@ -579,6 +580,7 @@ def htmlHistorySearch(cssCache: {}, translate: {}, baseDir: str,
'search',
YTReplacementDomain,
showPublishedDateOnly,
peertubeInstances,
showIndividualPostIcons,
showIndividualPostIcons,
False, False, False)
@ -599,7 +601,8 @@ def htmlHashtagSearch(cssCache: {},
session, wfRequest: {}, personCache: {},
httpPrefix: str, projectVersion: str,
YTReplacementDomain: str,
showPublishedDateOnly: bool) -> str:
showPublishedDateOnly: bool,
peertubeInstances: []) -> str:
"""Show a page containing search results for a hashtag
"""
if hashtag.startswith('#'):
@ -745,6 +748,7 @@ def htmlHashtagSearch(cssCache: {},
'search',
YTReplacementDomain,
showPublishedDateOnly,
peertubeInstances,
showRepeats, showIcons,
manuallyApprovesFollowers,
showPublicOnly,

View File

@ -61,7 +61,8 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
publishButtonAtTop: bool,
authorized: bool,
moderationActionStr: str,
theme: str) -> str:
theme: str,
peertubeInstances: []) -> str:
"""Show the timeline as html
"""
enableTimingLog = False
@ -566,6 +567,7 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
boxName,
YTReplacementDomain,
showPublishedDateOnly,
peertubeInstances,
boxName != 'dm',
showIndividualPostIcons,
manuallyApproveFollowers,
@ -720,7 +722,8 @@ def htmlShares(cssCache: {}, defaultTimeline: str,
iconsAsButtons: bool,
rssIconAtTop: bool,
publishButtonAtTop: bool,
authorized: bool, theme: str) -> str:
authorized: bool, theme: str,
peertubeInstances: []) -> str:
"""Show the shares timeline as html
"""
manuallyApproveFollowers = \
@ -739,7 +742,7 @@ def htmlShares(cssCache: {}, defaultTimeline: str,
positiveVoting, showPublishAsIcon,
fullWidthTimelineButtonHeader,
iconsAsButtons, rssIconAtTop, publishButtonAtTop,
authorized, None, theme)
authorized, None, theme, peertubeInstances)
def htmlInbox(cssCache: {}, defaultTimeline: str,
@ -757,7 +760,8 @@ def htmlInbox(cssCache: {}, defaultTimeline: str,
iconsAsButtons: bool,
rssIconAtTop: bool,
publishButtonAtTop: bool,
authorized: bool, theme: str) -> str:
authorized: bool, theme: str,
peertubeInstances: []) -> str:
"""Show the inbox as html
"""
manuallyApproveFollowers = \
@ -776,7 +780,7 @@ def htmlInbox(cssCache: {}, defaultTimeline: str,
positiveVoting, showPublishAsIcon,
fullWidthTimelineButtonHeader,
iconsAsButtons, rssIconAtTop, publishButtonAtTop,
authorized, None, theme)
authorized, None, theme, peertubeInstances)
def htmlBookmarks(cssCache: {}, defaultTimeline: str,
@ -794,7 +798,8 @@ def htmlBookmarks(cssCache: {}, defaultTimeline: str,
iconsAsButtons: bool,
rssIconAtTop: bool,
publishButtonAtTop: bool,
authorized: bool, theme: str) -> str:
authorized: bool, theme: str,
peertubeInstances: []) -> str:
"""Show the bookmarks as html
"""
manuallyApproveFollowers = \
@ -813,7 +818,7 @@ def htmlBookmarks(cssCache: {}, defaultTimeline: str,
positiveVoting, showPublishAsIcon,
fullWidthTimelineButtonHeader,
iconsAsButtons, rssIconAtTop, publishButtonAtTop,
authorized, None, theme)
authorized, None, theme, peertubeInstances)
def htmlEvents(cssCache: {}, defaultTimeline: str,
@ -831,7 +836,8 @@ def htmlEvents(cssCache: {}, defaultTimeline: str,
iconsAsButtons: bool,
rssIconAtTop: bool,
publishButtonAtTop: bool,
authorized: bool, theme: str) -> str:
authorized: bool, theme: str,
peertubeInstances: []) -> str:
"""Show the events as html
"""
manuallyApproveFollowers = \
@ -850,7 +856,7 @@ def htmlEvents(cssCache: {}, defaultTimeline: str,
positiveVoting, showPublishAsIcon,
fullWidthTimelineButtonHeader,
iconsAsButtons, rssIconAtTop, publishButtonAtTop,
authorized, None, theme)
authorized, None, theme, peertubeInstances)
def htmlInboxDMs(cssCache: {}, defaultTimeline: str,
@ -868,7 +874,8 @@ def htmlInboxDMs(cssCache: {}, defaultTimeline: str,
iconsAsButtons: bool,
rssIconAtTop: bool,
publishButtonAtTop: bool,
authorized: bool, theme: str) -> str:
authorized: bool, theme: str,
peertubeInstances: []) -> str:
"""Show the DM timeline as html
"""
return htmlTimeline(cssCache, defaultTimeline,
@ -882,7 +889,7 @@ def htmlInboxDMs(cssCache: {}, defaultTimeline: str,
showPublishAsIcon,
fullWidthTimelineButtonHeader,
iconsAsButtons, rssIconAtTop, publishButtonAtTop,
authorized, None, theme)
authorized, None, theme, peertubeInstances)
def htmlInboxReplies(cssCache: {}, defaultTimeline: str,
@ -900,7 +907,8 @@ def htmlInboxReplies(cssCache: {}, defaultTimeline: str,
iconsAsButtons: bool,
rssIconAtTop: bool,
publishButtonAtTop: bool,
authorized: bool, theme: str) -> str:
authorized: bool, theme: str,
peertubeInstances: []) -> str:
"""Show the replies timeline as html
"""
return htmlTimeline(cssCache, defaultTimeline,
@ -915,7 +923,7 @@ def htmlInboxReplies(cssCache: {}, defaultTimeline: str,
positiveVoting, showPublishAsIcon,
fullWidthTimelineButtonHeader,
iconsAsButtons, rssIconAtTop, publishButtonAtTop,
authorized, None, theme)
authorized, None, theme, peertubeInstances)
def htmlInboxMedia(cssCache: {}, defaultTimeline: str,
@ -933,7 +941,8 @@ def htmlInboxMedia(cssCache: {}, defaultTimeline: str,
iconsAsButtons: bool,
rssIconAtTop: bool,
publishButtonAtTop: bool,
authorized: bool, theme: str) -> str:
authorized: bool, theme: str,
peertubeInstances: []) -> str:
"""Show the media timeline as html
"""
return htmlTimeline(cssCache, defaultTimeline,
@ -948,7 +957,7 @@ def htmlInboxMedia(cssCache: {}, defaultTimeline: str,
positiveVoting, showPublishAsIcon,
fullWidthTimelineButtonHeader,
iconsAsButtons, rssIconAtTop, publishButtonAtTop,
authorized, None, theme)
authorized, None, theme, peertubeInstances)
def htmlInboxBlogs(cssCache: {}, defaultTimeline: str,
@ -966,7 +975,8 @@ def htmlInboxBlogs(cssCache: {}, defaultTimeline: str,
iconsAsButtons: bool,
rssIconAtTop: bool,
publishButtonAtTop: bool,
authorized: bool, theme: str) -> str:
authorized: bool, theme: str,
peertubeInstances: []) -> str:
"""Show the blogs timeline as html
"""
return htmlTimeline(cssCache, defaultTimeline,
@ -981,7 +991,7 @@ def htmlInboxBlogs(cssCache: {}, defaultTimeline: str,
positiveVoting, showPublishAsIcon,
fullWidthTimelineButtonHeader,
iconsAsButtons, rssIconAtTop, publishButtonAtTop,
authorized, None, theme)
authorized, None, theme, peertubeInstances)
def htmlInboxFeatures(cssCache: {}, defaultTimeline: str,
@ -1000,7 +1010,8 @@ def htmlInboxFeatures(cssCache: {}, defaultTimeline: str,
rssIconAtTop: bool,
publishButtonAtTop: bool,
authorized: bool,
theme: str) -> str:
theme: str,
peertubeInstances: []) -> str:
"""Show the features timeline as html
"""
return htmlTimeline(cssCache, defaultTimeline,
@ -1015,7 +1026,7 @@ def htmlInboxFeatures(cssCache: {}, defaultTimeline: str,
positiveVoting, showPublishAsIcon,
fullWidthTimelineButtonHeader,
iconsAsButtons, rssIconAtTop, publishButtonAtTop,
authorized, None, theme)
authorized, None, theme, peertubeInstances)
def htmlInboxNews(cssCache: {}, defaultTimeline: str,
@ -1033,7 +1044,8 @@ def htmlInboxNews(cssCache: {}, defaultTimeline: str,
iconsAsButtons: bool,
rssIconAtTop: bool,
publishButtonAtTop: bool,
authorized: bool, theme: str) -> str:
authorized: bool, theme: str,
peertubeInstances: []) -> str:
"""Show the news timeline as html
"""
return htmlTimeline(cssCache, defaultTimeline,
@ -1048,7 +1060,7 @@ def htmlInboxNews(cssCache: {}, defaultTimeline: str,
positiveVoting, showPublishAsIcon,
fullWidthTimelineButtonHeader,
iconsAsButtons, rssIconAtTop, publishButtonAtTop,
authorized, None, theme)
authorized, None, theme, peertubeInstances)
def htmlOutbox(cssCache: {}, defaultTimeline: str,
@ -1066,7 +1078,8 @@ def htmlOutbox(cssCache: {}, defaultTimeline: str,
iconsAsButtons: bool,
rssIconAtTop: bool,
publishButtonAtTop: bool,
authorized: bool, theme: str) -> str:
authorized: bool, theme: str,
peertubeInstances: []) -> str:
"""Show the Outbox as html
"""
manuallyApproveFollowers = \
@ -1082,4 +1095,4 @@ def htmlOutbox(cssCache: {}, defaultTimeline: str,
newswire, False, False, positiveVoting,
showPublishAsIcon, fullWidthTimelineButtonHeader,
iconsAsButtons, rssIconAtTop, publishButtonAtTop,
authorized, None, theme)
authorized, None, theme, peertubeInstances)