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

merge-requests/30/head
Bob Mottram 2021-02-08 12:09:24 +00:00
commit a0916dea8f
64 changed files with 798 additions and 176 deletions

View File

@ -230,6 +230,7 @@ from media import removeMetaData
from cache import storePersonInCache
from cache import getPersonFromCache
from httpsig import verifyPostHeaders
from theme import getTextModeBanner
from theme import setNewsAvatar
from theme import setTheme
from theme import getTheme
@ -2078,7 +2079,8 @@ class PubServer(BaseHTTPRequestHandler):
domainFull,
self.server.defaultTimeline,
self.server.newswire,
self.server.themeName).encode('utf-8')
self.server.themeName,
True).encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
cookie, callingDomain)
@ -2176,7 +2178,8 @@ class PubServer(BaseHTTPRequestHandler):
domainFull,
self.server.defaultTimeline,
self.server.newswire,
self.server.themeName).encode('utf-8')
self.server.themeName,
True).encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
cookie, callingDomain)
@ -4169,6 +4172,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.themeName = fields['themeDropdown']
setTheme(baseDir, self.server.themeName, domain,
allowLocalNetworkAccess)
self.server.textModeBanner = \
getTextModeBanner(self.server.baseDir)
self.server.iconsCache = {}
self.server.fontsCache = {}
self.server.showPublishAsIcon = \
@ -4618,6 +4623,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.themeName = currTheme
setTheme(baseDir, currTheme, domain,
self.server.allowLocalNetworkAccess)
self.server.textModeBanner = \
getTextModeBanner(self.server.baseDir)
self.server.iconsCache = {}
self.server.fontsCache = {}
self.server.showPublishAsIcon = \
@ -7023,6 +7030,7 @@ class PubServer(BaseHTTPRequestHandler):
self.server.dormantMonths,
self.server.peertubeInstances,
self.server.allowLocalNetworkAccess,
self.server.textModeBanner,
actorJson['roles'],
None, None)
msg = msg.encode('utf-8')
@ -7110,6 +7118,7 @@ class PubServer(BaseHTTPRequestHandler):
self.server.dormantMonths,
self.server.peertubeInstances,
allowLocalNetworkAccess,
self.server.textModeBanner,
actorJson['skills'],
None, None)
msg = msg.encode('utf-8')
@ -7501,7 +7510,8 @@ class PubServer(BaseHTTPRequestHandler):
authorized,
self.server.themeName,
self.server.peertubeInstances,
self.server.allowLocalNetworkAccess)
self.server.allowLocalNetworkAccess,
self.server.textModeBanner)
if GETstartTime:
self._benchmarkGETtimings(GETstartTime, GETtimings,
'show status done',
@ -7629,7 +7639,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.publishButtonAtTop,
authorized, self.server.themeName,
self.server.peertubeInstances,
self.server.allowLocalNetworkAccess)
self.server.allowLocalNetworkAccess,
self.server.textModeBanner)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@ -7750,7 +7761,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.publishButtonAtTop,
authorized, self.server.themeName,
self.server.peertubeInstances,
self.server.allowLocalNetworkAccess)
self.server.allowLocalNetworkAccess,
self.server.textModeBanner)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@ -7872,7 +7884,8 @@ class PubServer(BaseHTTPRequestHandler):
authorized,
self.server.themeName,
self.server.peertubeInstances,
self.server.allowLocalNetworkAccess)
self.server.allowLocalNetworkAccess,
self.server.textModeBanner)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@ -7994,7 +8007,8 @@ class PubServer(BaseHTTPRequestHandler):
authorized,
self.server.themeName,
self.server.peertubeInstances,
self.server.allowLocalNetworkAccess)
self.server.allowLocalNetworkAccess,
self.server.textModeBanner)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@ -8125,7 +8139,8 @@ class PubServer(BaseHTTPRequestHandler):
authorized,
self.server.themeName,
self.server.peertubeInstances,
self.server.allowLocalNetworkAccess)
self.server.allowLocalNetworkAccess,
self.server.textModeBanner)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@ -8252,7 +8267,8 @@ class PubServer(BaseHTTPRequestHandler):
authorized,
self.server.themeName,
self.server.peertubeInstances,
self.server.allowLocalNetworkAccess)
self.server.allowLocalNetworkAccess,
self.server.textModeBanner)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@ -8340,7 +8356,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.publishButtonAtTop,
authorized, self.server.themeName,
self.server.peertubeInstances,
self.server.allowLocalNetworkAccess)
self.server.allowLocalNetworkAccess,
self.server.textModeBanner)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@ -8445,7 +8462,8 @@ class PubServer(BaseHTTPRequestHandler):
authorized,
self.server.themeName,
self.server.peertubeInstances,
self.server.allowLocalNetworkAccess)
self.server.allowLocalNetworkAccess,
self.server.textModeBanner)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@ -8570,7 +8588,8 @@ class PubServer(BaseHTTPRequestHandler):
authorized,
self.server.themeName,
self.server.peertubeInstances,
self.server.allowLocalNetworkAccess)
self.server.allowLocalNetworkAccess,
self.server.textModeBanner)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@ -8687,7 +8706,8 @@ class PubServer(BaseHTTPRequestHandler):
authorized,
self.server.themeName,
self.server.peertubeInstances,
self.server.allowLocalNetworkAccess)
self.server.allowLocalNetworkAccess,
self.server.textModeBanner)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@ -8794,7 +8814,8 @@ class PubServer(BaseHTTPRequestHandler):
authorized, moderationActionStr,
self.server.themeName,
self.server.peertubeInstances,
self.server.allowLocalNetworkAccess)
self.server.allowLocalNetworkAccess,
self.server.textModeBanner)
msg = msg.encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@ -8895,6 +8916,7 @@ class PubServer(BaseHTTPRequestHandler):
self.server.dormantMonths,
self.server.peertubeInstances,
self.server.allowLocalNetworkAccess,
self.server.textModeBanner,
shares,
pageNumber, sharesPerPage)
msg = msg.encode('utf-8')
@ -8992,6 +9014,7 @@ class PubServer(BaseHTTPRequestHandler):
self.server.dormantMonths,
self.server.peertubeInstances,
self.server.allowLocalNetworkAccess,
self.server.textModeBanner,
following,
pageNumber,
followsPerPage).encode('utf-8')
@ -9089,6 +9112,7 @@ class PubServer(BaseHTTPRequestHandler):
self.server.dormantMonths,
self.server.peertubeInstances,
self.server.allowLocalNetworkAccess,
self.server.textModeBanner,
followers,
pageNumber,
followsPerPage).encode('utf-8')
@ -9209,6 +9233,7 @@ class PubServer(BaseHTTPRequestHandler):
self.server.dormantMonths,
self.server.peertubeInstances,
self.server.allowLocalNetworkAccess,
self.server.textModeBanner,
None, None).encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen,
@ -9711,7 +9736,8 @@ class PubServer(BaseHTTPRequestHandler):
inReplyToUrl: str, replyToList: [],
shareDescription: str, replyPageNumber: int,
domain: str, domainFull: str,
GETstartTime, GETtimings: {}, cookie) -> bool:
GETstartTime, GETtimings: {}, cookie,
noDropDown: bool) -> bool:
"""Shows the new post screen
"""
isNewPostEndpoint = False
@ -9740,7 +9766,8 @@ class PubServer(BaseHTTPRequestHandler):
domainFull,
self.server.defaultTimeline,
self.server.newswire,
self.server.themeName).encode('utf-8')
self.server.themeName,
noDropDown).encode('utf-8')
if not msg:
print('Error replying to ' + inReplyToUrl)
self._404()
@ -9773,7 +9800,8 @@ class PubServer(BaseHTTPRequestHandler):
httpPrefix,
self.server.defaultTimeline,
self.server.themeName,
peertubeInstances).encode('utf-8')
peertubeInstances,
self.server.textModeBanner).encode('utf-8')
if msg:
msglen = len(msg)
self._set_headers('text/html', msglen,
@ -9983,6 +10011,12 @@ class PubServer(BaseHTTPRequestHandler):
if self.path.startswith('/@'):
self.path = self.path.replace('/@', '/users/')
# turn off dropdowns on new post screen
noDropDown = False
if self.path.endswith('?nodropdown'):
noDropDown = True
self.path = self.path.replace('?nodropdown', '')
# redirect music to #nowplaying list
if self.path == '/music' or self.path == '/nowplaying':
self.path = '/tags/nowplaying'
@ -11009,7 +11043,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.baseDir, self.path,
self.server.domain,
self.server.defaultTimeline,
self.server.themeName).encode('utf-8')
self.server.themeName,
self.server.textModeBanner).encode('utf-8')
msglen = len(msg)
self._set_headers('text/html', msglen, cookie, callingDomain)
self._write(msg)
@ -11556,7 +11591,7 @@ class PubServer(BaseHTTPRequestHandler):
self.server.domain,
self.server.domainFull,
GETstartTime, GETtimings,
cookie):
cookie, noDropDown):
return
self._benchmarkGETtimings(GETstartTime, GETtimings,
@ -13850,6 +13885,9 @@ def runDaemon(verifyAllSignatures: bool,
print('ERROR: HTTP server failed to start. ' + str(e))
return False
# ASCII/ANSI text banner used in shell browsers, such as Lynx
httpd.textModeBanner = getTextModeBanner(baseDir)
httpd.unitTest = unitTest
httpd.allowLocalNetworkAccess = allowLocalNetworkAccess
if unitTest:

View File

@ -94,6 +94,14 @@ form {
border-radius: var(--form-border-radius);
}
.transparent {
color: transparent;
background: transparent;
font-size: 0px;
line-height: 0px;
height: 0px;
}
input[type=text], input[type=password] {
width: 100%;
padding: 12px 20px;

View File

@ -702,6 +702,15 @@ input[type=number] {
height: 0px;
}
.transparent hr {
border: 0;
color: transparent;
background: transparent;
font-size: 0px;
line-height: 0px;
height: 0px;
}
.labelsright {
float: right;
font-size: var(--font-size);
@ -1145,7 +1154,7 @@ div.container {
filter: brightness(var(--icon-brightness-change));
}
.col-right img.rightColEdit {
float: right;
float: right;
background: transparent;
width: var(--column-right-icon-size);
}

View File

@ -102,6 +102,14 @@ a:focus {
border: 2px solid var(--focus-color);
}
.transparent {
color: transparent;
background: transparent;
font-size: 0px;
line-height: 0px;
height: 0px;
}
.cw {
font-style: var(--cw-style);
color: var(--cw-color);

8
img/logo.txt 100644
View File

@ -0,0 +1,8 @@
8888888888 8888888b. 8888888 .d8888b. Y88b d88P .d88888b. 888b 888
888 888 Y88b 888 d88P Y88b Y88b d88P d88P" "Y88b 8888b 888
888 888 888 888 888 888 Y88o88P 888 888 88888b 888
8888888 888 d88P 888 888 Y888P 888 888 888Y88b 888
888 8888888P" 888 888 888 888 888 888 Y88b888
888 888 888 888 888 888 888 888 888 Y88888
888 888 888 Y88b d88P 888 Y88b. .d88P 888 Y8888
8888888888 888 8888888 "Y8888P" 888 "Y88888P" 888 Y888

View File

@ -25,11 +25,10 @@ scripts =
deploy/i2p
deploy/onion
install_requires =
crypto >= 1.4.1, < 2
idna >= 2.5, < 3
numpy >= 1.20.0, < 2
pillow >= 8.1.0, < 9
cryptography
cryptography >= 3.3.1, < 4
pyqrcode >= 1.2.1, < 2
python-dateutil >= 2.8.1, < 3
requests >= 2.25.1, < 3

View File

@ -15,6 +15,8 @@ from content import dangerousCSS
def _getThemeFiles() -> []:
"""Gets the list of theme style sheets
"""
return ('epicyon.css', 'login.css', 'follow.css',
'suspended.css', 'calendar.css', 'blog.css',
'options.css', 'search.css', 'links.css')
@ -39,6 +41,8 @@ def getThemesList(baseDir: str) -> []:
def _setThemeInConfig(baseDir: str, name: str) -> bool:
"""Sets the theme with the given name within config.json
"""
configFilename = baseDir + '/config.json'
if not os.path.isfile(configFilename):
return False
@ -118,6 +122,8 @@ def _setFullWidthTimelineButtonHeader(baseDir: str, fullWidth: bool) -> bool:
def getTheme(baseDir: str) -> str:
"""Gets the current theme name from config.json
"""
configFilename = baseDir + '/config.json'
if os.path.isfile(configFilename):
configJson = loadJson(configFilename, 0)
@ -128,6 +134,8 @@ def getTheme(baseDir: str) -> str:
def _removeTheme(baseDir: str):
"""Removes the current theme style sheets
"""
themeFiles = _getThemeFiles()
for filename in themeFiles:
if os.path.isfile(baseDir + '/' + filename):
@ -422,6 +430,63 @@ def _setThemeFonts(baseDir: str, themeName: str) -> None:
break
def getTextModeBanner(baseDir: str) -> str:
"""Returns the banner used for shell browsers, like Lynx
"""
textModeBannerFilename = baseDir + '/accounts/banner.txt'
if os.path.isfile(textModeBannerFilename):
with open(textModeBannerFilename, 'r') as fp:
bannerStr = fp.read()
if bannerStr:
return bannerStr.replace('\n', '<br>')
return None
def getTextModeLogo(baseDir: str) -> str:
"""Returns the login screen logo used for shell browsers, like Lynx
"""
textModeLogoFilename = baseDir + '/accounts/logo.txt'
if not os.path.isfile(textModeLogoFilename):
textModeLogoFilename = baseDir + '/img/logo.txt'
with open(textModeLogoFilename, 'r') as fp:
logoStr = fp.read()
if logoStr:
return logoStr.replace('\n', '<br>')
return None
def _setTextModeTheme(baseDir: str, name: str) -> None:
# set the text mode logo which appears on the login screen
# in browsers such as Lynx
textModeLogoFilename = \
baseDir + '/theme/' + name + '/logo.txt'
if os.path.isfile(textModeLogoFilename):
try:
copyfile(textModeLogoFilename,
baseDir + '/accounts/logo.txt')
except BaseException:
pass
else:
try:
copyfile(baseDir + '/img/logo.txt',
baseDir + '/accounts/logo.txt')
except BaseException:
pass
# set the text mode banner which appears in browsers such as Lynx
textModeBannerFilename = \
baseDir + '/theme/' + name + '/banner.txt'
if os.path.isfile(baseDir + '/accounts/banner.txt'):
os.remove(baseDir + '/accounts/banner.txt')
if os.path.isfile(textModeBannerFilename):
try:
copyfile(textModeBannerFilename,
baseDir + '/accounts/banner.txt')
except BaseException:
pass
def _setThemeImages(baseDir: str, name: str) -> None:
"""Changes the profile background image
and banner to the defaults
@ -439,6 +504,8 @@ def _setThemeImages(baseDir: str, name: str) -> None:
rightColImageFilename = \
baseDir + '/theme/' + themeNameLower + '/right_col_image.png'
_setTextModeTheme(baseDir, themeNameLower)
backgroundNames = ('login', 'shares', 'delete', 'follow',
'options', 'block', 'search', 'calendar')
extensions = getImageExtensions()
@ -554,6 +621,8 @@ def setNewsAvatar(baseDir: str, name: str,
def setTheme(baseDir: str, name: str, domain: str,
allowLocalNetworkAccess: bool) -> bool:
"""Sets the theme with the given name as the current theme
"""
result = False
prevThemeName = getTheme(baseDir)

View File

@ -0,0 +1,10 @@
88888888888 88
88 ""
88
88aaaaa 8b,dPPYba, 88 ,adPPYba, 8b d8 ,adPPYba, 8b,dPPYba,
88""""" 88P' "8a 88 a8" "" `8b d8' a8" "8a 88P' `"8a
88 88 d8 88 8b `8b d8' 8b d8 88 88
88 88b, ,a8" 88 "8a, ,aa `8b,d8' "8a, ,a8" 88 88
88888888888 88`YbbdP"' 88 `"Ybbd8"' Y88' `"YbbdP"' 88 88
88 d8'
88 d8'

View File

@ -0,0 +1,10 @@
88888888888 88
88 ""
88
88aaaaa 8b,dPPYba, 88 ,adPPYba, 8b d8 ,adPPYba, 8b,dPPYba,
88""""" 88P' "8a 88 a8" "" `8b d8' a8" "8a 88P' `"8a
88 88 d8 88 8b `8b d8' 8b d8 88 88
88 88b, ,a8" 88 "8a, ,aa `8b,d8' "8a, ,a8" 88 88
88888888888 88`YbbdP"' 88 `"Ybbd8"' Y88' `"YbbdP"' 88 88
88 d8'
88 d8'

View File

@ -0,0 +1,11 @@
_, _, ,'`.
`$$' `$$' `. ,' E P I C Y O N
$$ $$ `'
$$ $$ _, _
,d$$$g$$ ,d$$$b. $$,d$$$b.`$$' g$$$$$b.`$$,d$$b.
,$P' `$$ ,$P' `Y$. $$$' `$$ $$ "' `$$ $$$' `$$
$$' $$ $$' `$$ $$' $$ $$ ,ggggg$$ $$' $$
$$ $$ $$ggggg$$ $$ $$ $$ ,$P" $$ $$ $$
$$ ,$$ $$. $$ ,$P $$ $$' ,$$ $$ $$
`$g. ,$$$ `$$._ _., $$ _,g$P' $$ `$b. ,$$$ $$ $$
`Y$$P'$$. `Y$$$$P',$$$$P"' ,$$. `Y$$P'$$.$$. ,$$.

View File

@ -0,0 +1,14 @@
_,met$$$$$gg.
,g$$$$$$$$$$$$$$$P.
,g$$P"" """Y$$.".
,$$P' `$$$.
,$$P ,ggs. `$$b:
`d$$' ,$P"' . $$$
$$P d$' , $$P
$$: $$. - ,d$$'
$$; Y$b._ _,d$P'
Y$$. `.`"Y$$$$P"'
`$$b "-.__
`Y$$b
`$$. E P I C Y O N
`$.

View File

@ -0,0 +1,8 @@
__
, ," e`--o
(( ( | __,'
\\~-------' \_;/
( /
/) .______. )
(( ( (( (
``-' ``-'

View File

@ -0,0 +1,8 @@
8888888888 8888888b. 8888888 .d8888b. Y88b d88P .d88888b. 888b 888
888 888 Y88b 888 d88P Y88b Y88b d88P d88P" "Y88b 8888b 888
888 888 888 888 888 888 Y88o88P 888 888 88888b 888
8888888 888 d88P 888 888 Y888P 888 888 888Y88b 888
888 8888888P" 888 888 888 888 888 888 Y88b888
888 888 888 888 888 888 888 888 888 Y88888
888 888 888 Y88b d88P 888 Y88b. .d88P 888 Y8888
8888888888 888 8888888 "Y8888P" 888 "Y88888P" 888 Y888

View File

@ -0,0 +1,10 @@
88888888888 88
88 ""
88
88aaaaa 8b,dPPYba, 88 ,adPPYba, 8b d8 ,adPPYba, 8b,dPPYba,
88""""" 88P' "8a 88 a8" "" `8b d8' a8" "8a 88P' `"8a
88 88 d8 88 8b `8b d8' 8b d8 88 88
88 88b, ,a8" 88 "8a, ,aa `8b,d8' "8a, ,a8" 88 88
88888888888 88`YbbdP"' 88 `"Ybbd8"' Y88' `"YbbdP"' 88 88
88 d8'
88 d8'

View File

@ -0,0 +1,10 @@
88888888888 88
88 ""
88
88aaaaa 8b,dPPYba, 88 ,adPPYba, 8b d8 ,adPPYba, 8b,dPPYba,
88""""" 88P' "8a 88 a8" "" `8b d8' a8" "8a 88P' `"8a
88 88 d8 88 8b `8b d8' 8b d8 88 88
88 88b, ,a8" 88 "8a, ,aa `8b,d8' "8a, ,a8" 88 88
88888888888 88`YbbdP"' 88 `"Ybbd8"' Y88' `"YbbdP"' 88 88
88 d8'
88 d8'

View File

@ -0,0 +1,7 @@
____ _________ __ _____ ___ ____
/ _]| \ | / ]| | | / \ | \
| [_ | o ) | / / | | || || _ |
| _]| _/| |/ / | ~ || O || | |
| [_ | | | / \_ |___, || || | |
| || | | \ || || || | |
|_____||__| |____\____||____/ \___/ |__|__|

View File

@ -0,0 +1,7 @@
____ _________ __ _____ ___ ____
/ _]| \ | / ]| | | / \ | \
| [_ | o ) | / / | | || || _ |
| _]| _/| |/ / | ~ || O || | |
| [_ | | | / \_ |___, || || | |
| || | | \ || || || | |
|_____||__| |____\____||____/ \___/ |__|__|

View File

@ -0,0 +1,8 @@
/ \
| / ,--. \ |
\ \ `--' / /
,------.,------. ,--. ,-----.,--. ,--.,-----. ,--. ,--.
| .---'| .--. '| |' .--./ \ `.' /' .-. '| ,'.| |
| `--, | '--' || || | '. / | | | || |' ' |
| `---.| | --' | |' '--'\ | | ' '-' '| | ` |
`------'`--' `--' `-----' `--' `-----' `--' `--'

View File

@ -0,0 +1,8 @@
/ \
| / ,--. \ |
\ \ `--' / /
,------.,------. ,--. ,-----.,--. ,--.,-----. ,--. ,--.
| .---'| .--. '| |' .--./ \ `.' /' .-. '| ,'.| |
| `--, | '--' || || | '. / | | | || |' ' |
| `---.| | --' | |' '--'\ | | ' '-' '| | ` |
`------'`--' `--' `-----' `--' `-----' `--' `--'

View File

@ -0,0 +1,8 @@
/ \
| / ,--. \ |
\ \ `--' / /
,------.,------. ,--. ,-----.,--. ,--.,-----. ,--. ,--.
| .---'| .--. '| |' .--./ \ `.' /' .-. '| ,'.| |
| `--, | '--' || || | '. / | | | || |' ' |
| `---.| | --' | |' '--'\ | | ' '-' '| | ` |
`------'`--' `--' `-----' `--' `-----' `--' `--'

View File

@ -0,0 +1,8 @@
/ \
| / ,--. \ |
\ \ `--' / /
,------.,------. ,--. ,-----.,--. ,--.,-----. ,--. ,--.
| .---'| .--. '| |' .--./ \ `.' /' .-. '| ,'.| |
| `--, | '--' || || | '. / | | | || |' ' |
| `---.| | --' | |' '--'\ | | ' '-' '| | ` |
`------'`--' `--' `-----' `--' `-----' `--' `--'

View File

@ -0,0 +1,6 @@
_______ ______ _____ ______ _ _ _____ ______
(_______|_____ (_____) _____) | | |/ ___ \| ___ \
_____ _____) ) _ | / | |___| | | | | | | |
| ___) | ____/ | || | \_____/| | | | | | |
| |_____| | _| || \_____ ___ | |___| | | | |
|_______)_| (_____)______) (___) \_____/|_| |_|

View File

@ -0,0 +1,6 @@
_______ ______ _____ ______ _ _ _____ ______
(_______|_____ (_____) _____) | | |/ ___ \| ___ \
_____ _____) ) _ | / | |___| | | | | | | |
| ___) | ____/ | || | \_____/| | | | | | |
| |_____| | _| || \_____ ___ | |___| | | | |
|_______)_| (_____)______) (___) \_____/|_| |_|

View File

@ -0,0 +1,6 @@
88888888b 888888ba dP a88888b. dP dP .88888. 888888ba
88 88 `8b 88 d8' `88 Y8. .8P d8' `8b 88 `8b
a88aaaa a88aaaa8P' 88 88 Y8aa8P 88 88 88 88
88 88 88 88 88 88 88 88 88
88 88 88 Y8. .88 88 Y8. .8P 88 88
88888888P dP dP Y88888P' dP `8888P' dP dP

View File

@ -0,0 +1,6 @@
88888888b 888888ba dP a88888b. dP dP .88888. 888888ba
88 88 `8b 88 d8' `88 Y8. .8P d8' `8b 88 `8b
a88aaaa a88aaaa8P' 88 88 Y8aa8P 88 88 88 88
88 88 88 88 88 88 88 88 88
88 88 88 Y8. .88 88 Y8. .8P 88 88
88888888P dP dP Y88888P' dP `8888P' dP dP

View File

@ -0,0 +1,14 @@
( (
) )
___ ___ ___ _____ _____ _ _ ........
| __| _ \_ _/ __\ \ / / _ \| \| | | |]
| _|| _/| | (__ \ V / (_) | .` | \ /
|___|_| |___\___| |_| \___/|_|\_| \____/

View File

@ -0,0 +1,14 @@
( (
) )
___ ___ ___ _____ _____ _ _ ........
| __| _ \_ _/ __\ \ / / _ \| \| | | |]
| _|| _/| | (__ \ V / (_) | .` | \ /
|___|_| |___\___| |_| \___/|_|\_| \____/

View File

@ -0,0 +1,8 @@
888888888b 888888ba dP a88888b. dP dP .88888. 888888ba
88 88 `8b 88 d8' `88 Y8. .8P d8' `8b 88 `8b
a88aaaa a88aaaa8P' 88 88 Y8aa8P 88 88 88 88
88 88 88 88 88 88 88 88 88
88 88 88 Y8. .88 88 Y8. .8P 88 88
88888888P' dP dP Y88888P' 88 `8888P' dP dP
88
dP

View File

@ -0,0 +1,8 @@
888888888b 888888ba dP a88888b. dP dP .88888. 888888ba
88 88 `8b 88 d8' `88 Y8. .8P d8' `8b 88 `8b
a88aaaa a88aaaa8P' 88 88 Y8aa8P 88 88 88 88
88 88 88 88 88 88 88 88 88
88 88 88 Y8. .88 88 Y8. .8P 88 88
88888888P' dP dP Y88888P' 88 `8888P' dP dP
88
dP

View File

@ -0,0 +1,8 @@
__
, ," e`--o
(( ( | __,'
\\~-------' \_;/
( /
/) .______. )
(( ( (( (
``-' ``-'

View File

@ -0,0 +1,8 @@
8888888888 8888888b. 8888888 .d8888b. Y88b d88P .d88888b. 888b 888
888 888 Y88b 888 d88P Y88b Y88b d88P d88P" "Y88b 8888b 888
888 888 888 888 888 888 Y88o88P 888 888 88888b 888
8888888 888 d88P 888 888 Y888P 888 888 888Y88b 888
888 8888888P" 888 888 888 888 888 888 Y88b888
888 888 888 888 888 888 888 888 888 Y88888
888 888 888 Y88b d88P 888 Y88b. .d88P 888 Y8888
8888888888 888 8888888 "Y8888P" 888 "Y88888P" 888 Y888

View File

@ -0,0 +1,9 @@
oooooooooooo o8o
`888' `8 `"'
888 oo.ooooo. oooo .ooooo. oooo ooo .ooooo. ooo. .oo.
888oooo8 888' `88b `888 d88' `"Y8 `88. .8' d88' `88b `888P"Y88b
888 " 888 888 888 888 `88..8' 888 888 888 888
888 o 888 888 888 888 .o8 `888' 888 888 888 888
o888ooooood8 888bod8P' o888o `Y8bod8P' .8' `Y8bod8P' o888o o888o
888 .o..P'
o888o `Y8P'

View File

@ -0,0 +1,9 @@
oooooooooooo o8o
`888' `8 `"'
888 oo.ooooo. oooo .ooooo. oooo ooo .ooooo. ooo. .oo.
888oooo8 888' `88b `888 d88' `"Y8 `88. .8' d88' `88b `888P"Y88b
888 " 888 888 888 888 `88..8' 888 888 888 888
888 o 888 888 888 888 .o8 `888' 888 888 888 888
o888ooooood8 888bod8P' o888o `Y8bod8P' .8' `Y8bod8P' o888o o888o
888 .o..P'
o888o `Y8P'

View File

@ -0,0 +1,6 @@
_______ ______ _____ ______ _ _ _____ ______
(_______|_____ (_____) _____) | | |/ ___ \| ___ \
_____ _____) ) _ | / | |___| | | | | | | |
| ___) | ____/ | || | \_____/| | | | | | |
| |_____| | _| || \_____ ___ | |___| | | | |
|_______)_| (_____)______) (___) \_____/|_| |_|

View File

@ -0,0 +1,6 @@
_______ ______ _____ ______ _ _ _____ ______
(_______|_____ (_____) _____) | | |/ ___ \| ___ \
_____ _____) ) _ | / | |___| | | | | | | |
| ___) | ____/ | || | \_____/| | | | | | |
| |_____| | _| || \_____ ___ | |___| | | | |
|_______)_| (_____)______) (___) \_____/|_| |_|

View File

@ -0,0 +1,6 @@
_______ _____ _____ _______ __ __ _____ __ _ _=_
| | | | | \ / | | |\ | q(-_-)p
|______ |_____| | | \_/ | | | \ | ,\___/\
| | | | | | | | \ | ( _ _ )
|______ | __|__ |_____ | |_____| | \| (_\_\_|_/_)

View File

@ -0,0 +1,6 @@
_______ _____ _____ _______ __ __ _____ __ _ _=_
| | | | | \ / | | |\ | q(-_-)p
|______ |_____| | | \_/ | | | \ | ,\___/\
| | | | | | | | \ | ( _ _ )
|______ | __|__ |_____ | |_____| | \| (_\_\_|_/_)

View File

@ -366,5 +366,6 @@
"Version": "الإصدار",
"Skip to timeline": "تخطي إلى الجدول الزمني",
"Skip to Newswire": "انتقل إلى Newswire",
"Skip to Links": "تخطي إلى روابط الويب"
"Skip to Links": "تخطي إلى روابط الويب",
"Publish a blog article": "نشر مقال بلوق"
}

View File

@ -366,5 +366,6 @@
"Version": "Versió",
"Skip to timeline": "Ves a la cronologia",
"Skip to Newswire": "Vés a Newswire",
"Skip to Links": "Vés als enllaços web"
"Skip to Links": "Vés als enllaços web",
"Publish a blog article": "Publicar un article del bloc"
}

View File

@ -366,5 +366,6 @@
"Version": "Fersiwn",
"Skip to timeline": "Neidio i'r llinell amser",
"Skip to Newswire": "Neidio i Newswire",
"Skip to Links": "Neidio i Dolenni Gwe"
"Skip to Links": "Neidio i Dolenni Gwe",
"Publish a blog article": "Cyhoeddi erthygl blog"
}

View File

@ -366,5 +366,6 @@
"Version": "Ausführung",
"Skip to timeline": "Zur Zeitleiste springen",
"Skip to Newswire": "Springe zu Newswire",
"Skip to Links": "Springe zu Weblinks"
"Skip to Links": "Springe zu Weblinks",
"Publish a blog article": "Veröffentlichen Sie einen Blog-Artikel"
}

View File

@ -366,5 +366,6 @@
"Version": "Version",
"Skip to timeline": "Skip to timeline",
"Skip to Newswire": "Skip to Newswire",
"Skip to Links": "Skip to Links"
"Skip to Links": "Skip to Links",
"Publish a blog article": "Publish a blog article"
}

View File

@ -366,5 +366,6 @@
"Version": "Versión",
"Skip to timeline": "Saltar a la línea de tiempo",
"Skip to Newswire": "Saltar a Newswire",
"Skip to Links": "Saltar a enlaces web"
"Skip to Links": "Saltar a enlaces web",
"Publish a blog article": "Publica un artículo de blog"
}

View File

@ -366,5 +366,6 @@
"Version": "Version",
"Skip to timeline": "Passer à la chronologie",
"Skip to Newswire": "Passer à Newswire",
"Skip to Links": "Passer aux liens Web"
"Skip to Links": "Passer aux liens Web",
"Publish a blog article": "Publier un article de blog"
}

View File

@ -366,5 +366,6 @@
"Version": "Leagan",
"Skip to timeline": "Scipeáil chuig an amlíne",
"Skip to Newswire": "Scipeáil chuig Newswire",
"Skip to Links": "Scipeáil chuig Naisc Ghréasáin"
"Skip to Links": "Scipeáil chuig Naisc Ghréasáin",
"Publish a blog article": "Foilsigh alt blagála"
}

View File

@ -366,5 +366,6 @@
"Version": "संस्करण",
"Skip to timeline": "टाइमलाइन पर जाएं",
"Skip to Newswire": "Newswire पर जाएं",
"Skip to Links": "वेब लिंक पर जाएं"
"Skip to Links": "वेब लिंक पर जाएं",
"Publish a blog article": "एक ब्लॉग लेख प्रकाशित करें"
}

View File

@ -366,5 +366,6 @@
"Version": "Versione",
"Skip to timeline": "Passa alla sequenza temporale",
"Skip to Newswire": "Passa a Newswire",
"Skip to Links": "Passa a collegamenti Web"
"Skip to Links": "Passa a collegamenti Web",
"Publish a blog article": "Pubblica un articolo sul blog"
}

View File

@ -366,5 +366,6 @@
"Version": "バージョン",
"Skip to timeline": "タイムラインにスキップ",
"Skip to Newswire": "Newswireにスキップ",
"Skip to Links": "Webリンクにスキップ"
"Skip to Links": "Webリンクにスキップ",
"Publish a blog article": "ブログ記事を公開する"
}

View File

@ -362,5 +362,6 @@
"Version": "Version",
"Skip to timeline": "Skip to timeline",
"Skip to Newswire": "Skip to Newswire",
"Skip to Links": "Skip to Links"
"Skip to Links": "Skip to Links",
"Publish a blog article": "Publish a blog article"
}

View File

@ -366,5 +366,6 @@
"Version": "Versão",
"Skip to timeline": "Pular para a linha do tempo",
"Skip to Newswire": "Pular para Newswire",
"Skip to Links": "Pular para links da web"
"Skip to Links": "Pular para links da web",
"Publish a blog article": "Publique um artigo de blog"
}

View File

@ -366,5 +366,6 @@
"Version": "Версия",
"Skip to timeline": "Перейти к временной шкале",
"Skip to Newswire": "Перейти к ленте новостей",
"Skip to Links": "Перейти к веб-ссылкам"
"Skip to Links": "Перейти к веб-ссылкам",
"Publish a blog article": "Опубликовать статью в блоге"
}

View File

@ -366,5 +366,6 @@
"Version": "版",
"Skip to timeline": "跳到时间线",
"Skip to Newswire": "跳到新闻专线",
"Skip to Links": "跳到网页链接"
"Skip to Links": "跳到网页链接",
"Publish a blog article": "发布博客文章"
}

View File

@ -494,10 +494,20 @@ def evilIncarnate() -> []:
def isEvil(domain: str) -> bool:
# https://www.youtube.com/watch?v=5qw1hcevmdU
if not isinstance(domain, str):
print('WARN: Malformed domain ' + str(domain))
return True
# https://www.youtube.com/watch?v=5qw1hcevmdU
# if a domain contains any of these strings then it is
# declaring itself to be hostile
evilEmporium = (
'nazi', 'extremis', 'extreemis', 'gendercritic',
'kiwifarm', 'illegal', 'raplst', 'rapist',
'antivax', 'plandemic'
)
for hostileStr in evilEmporium:
if hostileStr in domain:
return True
evilDomains = evilIncarnate()
for concentratedEvil in evilDomains:
if domain.endswith(concentratedEvil):

View File

@ -50,7 +50,8 @@ def getRightColumnContent(baseDir: str, nickname: str, domainFull: str,
publishButtonAtTop: bool,
authorized: bool,
showHeaderImage: bool,
theme: str) -> str:
theme: str,
defaultTimeline: str) -> str:
"""Returns html content for the right column
"""
htmlStr = ''
@ -62,10 +63,13 @@ def getRightColumnContent(baseDir: str, nickname: str, domainFull: str,
if authorized:
# only show the publish button if logged in, otherwise replace it with
# a login button
titleStr = translate['Publish a blog article']
if defaultTimeline == 'tlfeatures':
titleStr = translate['Publish a news article']
publishButtonStr = \
' <a href="' + \
'/users/' + nickname + '/newblog" ' + \
'title="' + translate['Publish a news article'] + '">' + \
'/users/' + nickname + '/newblog?nodropdown" ' + \
'title="' + titleStr + '">' + \
'<button class="publishbtn">' + \
translate['Publish'] + '</button></a>\n'
else:
@ -158,13 +162,16 @@ def getRightColumnContent(baseDir: str, nickname: str, domainFull: str,
# show publish icon at top
if showPublishButton:
if showPublishAsIcon:
titleStr = translate['Publish a blog article']
if defaultTimeline == 'tlfeatures':
titleStr = translate['Publish a news article']
htmlStr += \
' <a href="' + \
'/users/' + nickname + '/newblog">' + \
'/users/' + nickname + '/newblog?nodropdown">' + \
'<img class="' + editImageClass + \
'" loading="lazy" alt="' + \
translate['Publish a news article'] + '" title="' + \
translate['Publish a news article'] + '" src="/' + \
titleStr + '" title="' + \
titleStr + '" src="/' + \
'icons/publish.png" /></a>\n'
if editImageClass == 'rightColEdit':
@ -485,7 +492,8 @@ def htmlNewswireMobile(cssCache: {}, baseDir: str, nickname: str,
newswire, positiveVoting,
False, timelinePath, showPublishButton,
showPublishAsIcon, rssIconAtTop, False,
authorized, False, theme)
authorized, False, theme,
defaultTimeline)
else:
if editor:
htmlStr += '<br><br><br>\n'

View File

@ -71,19 +71,25 @@ def _htmlNewPostDropDown(scopeIcon: str, scopeDescription: str,
dropdownDMSuffix: str,
dropdownReminderSuffix: str,
dropdownEventSuffix: str,
dropdownReportSuffix: str) -> str:
dropdownReportSuffix: str,
noDropDown: bool) -> str:
"""Returns the html for a drop down list of new post types
"""
dropDownContent = '<nav><div class="newPostDropdown">\n'
dropDownContent += ' <input type="checkbox" ' + \
'id="my-newPostDropdown" value="" name="my-checkbox">\n'
if not noDropDown:
dropDownContent += ' <input type="checkbox" ' + \
'id="my-newPostDropdown" value="" name="my-checkbox">\n'
dropDownContent += ' <label for="my-newPostDropdown"\n'
dropDownContent += ' data-toggle="newPostDropdown">\n'
dropDownContent += ' <img loading="lazy" alt="" title="" src="/' + \
'icons/' + scopeIcon + '"/><b>' + \
scopeDescription + '</b></label>\n'
dropDownContent += ' <ul>\n'
if noDropDown:
dropDownContent += '</div></nav>\n'
return dropDownContent
dropDownContent += ' <ul>\n'
if showPublicOnDropdown:
dropDownContent += \
'<li><a href="' + pathBase + dropdownNewPostSuffix + \
@ -156,8 +162,8 @@ def _htmlNewPostDropDown(scopeIcon: str, scopeDescription: str,
'icons/scope_question.png"/><b>' + \
translate['Question'] + '</b><br>' + \
translate['Ask a question'] + '</a></li>\n'
dropDownContent += ' </ul>\n'
dropDownContent += '</div></nav>\n'
return dropDownContent
@ -171,7 +177,7 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {},
nickname: str, domain: str,
domainFull: str,
defaultTimeline: str, newswire: {},
theme: str) -> str:
theme: str, noDropDown: bool) -> str:
"""New post screen
"""
replyStr = ''
@ -641,7 +647,8 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {},
dropdownDMSuffix,
dropdownReminderSuffix,
dropdownEventSuffix,
dropdownReportSuffix)
dropdownReportSuffix,
noDropDown)
else:
if not shareDescription:
# reporting a post to moderator

View File

@ -169,7 +169,8 @@ def htmlFrontScreen(rssIconAtTop: bool,
httpPrefix, translate,
False, False, newswire, False,
False, None, False, False,
False, True, authorized, True, theme)
False, True, authorized, True, theme,
defaultTimeline)
profileFooterStr += ' </td>\n'
profileFooterStr += ' </tr>\n'
profileFooterStr += ' </tbody>\n'

View File

@ -53,25 +53,25 @@ def headerButtonsTimeline(defaultTimeline: str,
if defaultTimeline == 'tlmedia':
tlStr += \
'<a href="' + usersPath + \
'/tlmedia"><button class="' + \
'/tlmedia" tabindex="-1"><button class="' + \
mediaButton + '"><span>' + translate['Media'] + \
'</span></button></a>'
elif defaultTimeline == 'tlblogs':
tlStr += \
'<a href="' + usersPath + \
'/tlblogs"><button class="' + \
'/tlblogs" tabindex="-1"><button class="' + \
blogsButton + '"><span>' + translate['Blogs'] + \
'</span></button></a>'
elif defaultTimeline == 'tlfeatures':
tlStr += \
'<a href="' + usersPath + \
'/tlfeatures"><button class="' + \
'/tlfeatures" tabindex="-1"><button class="' + \
featuresButton + '"><span>' + translate['Features'] + \
'</span></button></a>'
else:
tlStr += \
'<a href="' + usersPath + \
'/inbox"><button class="' + \
'/inbox" tabindex="-1"><button class="' + \
inboxButton + '"><span>' + \
translate['Inbox'] + '</span></button></a>'
@ -83,7 +83,7 @@ def headerButtonsTimeline(defaultTimeline: str,
if not featuresHeader:
tlStr += \
'<a href="' + usersPath + \
'/dm"><button class="' + dmButton + \
'/dm" tabindex="-1"><button class="' + dmButton + \
'"><span>' + htmlHighlightLabel(translate['DM'], newDM) + \
'</span></button></a>'
@ -92,8 +92,8 @@ def headerButtonsTimeline(defaultTimeline: str,
nickname + '@' + domain + '/tlreplies.index'
if os.path.isfile(repliesIndexFilename):
tlStr += \
'<a href="' + usersPath + '/tlreplies"><button class="' + \
repliesButton + '"><span>' + \
'<a href="' + usersPath + '/tlreplies" tabindex="-1">' + \
'<button class="' + repliesButton + '"><span>' + \
htmlHighlightLabel(translate['Replies'], newReply) + \
'</span></button></a>'
@ -102,14 +102,14 @@ def headerButtonsTimeline(defaultTimeline: str,
if not minimal and not featuresHeader:
tlStr += \
'<a href="' + usersPath + \
'/tlmedia"><button class="' + \
'/tlmedia" tabindex="-1"><button class="' + \
mediaButton + '"><span>' + translate['Media'] + \
'</span></button></a>'
else:
if not minimal:
tlStr += \
'<a href="' + usersPath + \
'/inbox"><button class="' + \
'/inbox" tabindex="-1"><button class="' + \
inboxButton+'"><span>' + translate['Inbox'] + \
'</span></button></a>'
@ -123,14 +123,14 @@ def headerButtonsTimeline(defaultTimeline: str,
titleStr = translate['Article']
tlStr += \
'<a href="' + usersPath + \
'/tlblogs"><button class="' + \
'/tlblogs" tabindex="-1"><button class="' + \
blogsButton + '"><span>' + titleStr + \
'</span></button></a>'
else:
if not minimal:
tlStr += \
'<a href="' + usersPath + \
'/inbox"><button class="' + \
'/inbox" tabindex="-1"><button class="' + \
inboxButton + '"><span>' + translate['Inbox'] + \
'</span></button></a>'
@ -140,7 +140,7 @@ def headerButtonsTimeline(defaultTimeline: str,
if not featuresHeader:
tlStr += \
'<a href="' + usersPath + \
'/inbox"><button class="' + \
'/inbox" tabindex="-1"><button class="' + \
inboxButton + '"><span>' + translate['Inbox'] + \
'</span></button></a>'
@ -155,14 +155,14 @@ def headerButtonsTimeline(defaultTimeline: str,
happeningStr += \
'<a href="' + usersPath + '/calendar?year=' + \
str(now.year) + '?month=' + str(now.month) + \
'?day=' + str(now.day) + '">' + \
'?day=' + str(now.day) + '" tabindex="-1">' + \
'<button class="buttonevent">' + \
translate['Happening Today'] + '</button></a>'
else:
happeningStr += \
'<a href="' + usersPath + '/calendar?year=' + \
str(now.year) + '?month=' + str(now.month) + \
'?day=' + str(now.day) + '">' + \
'?day=' + str(now.day) + '" tabindex="-1">' + \
'<button class="button">' + \
translate['Happening Today'] + '</button></a>'
@ -171,12 +171,14 @@ def headerButtonsTimeline(defaultTimeline: str,
if not iconsAsButtons:
happeningStr += \
'<a href="' + usersPath + \
'/calendar"><button class="buttonevent">' + \
'/calendar" tabindex="-1">' + \
'<button class="buttonevent">' + \
translate['Happening This Week'] + '</button></a>'
else:
happeningStr += \
'<a href="' + usersPath + \
'/calendar"><button class="button">' + \
'/calendar" tabindex="-1">' + \
'<button class="button">' + \
translate['Happening This Week'] + '</button></a>'
else:
# happening this week button
@ -184,12 +186,14 @@ def headerButtonsTimeline(defaultTimeline: str,
if not iconsAsButtons:
happeningStr += \
'<a href="' + usersPath + \
'/calendar"><button class="buttonevent">' + \
'/calendar" tabindex="-1">' + \
'<button class="buttonevent">' + \
translate['Happening This Week'] + '</button></a>'
else:
happeningStr += \
'<a href="' + usersPath + \
'/calendar"><button class="button">' + \
'/calendar" tabindex="-1">' + \
'<button class="button">' + \
translate['Happening This Week'] + '</button></a>'
if not featuresHeader:
@ -197,7 +201,8 @@ def headerButtonsTimeline(defaultTimeline: str,
tlStr += \
'<a href="' + usersPath + \
'/outbox"><button class="' + \
sentButton + '"><span>' + translate['Outbox'] + \
sentButton + '" tabindex="-1">' + \
'<span>' + translate['Outbox'] + \
'</span></button></a>'
# add other buttons
@ -219,7 +224,7 @@ def headerButtonsTimeline(defaultTimeline: str,
# the search button
tlStr += \
'<a href="' + usersPath + \
'/search"><button class="button">' + \
'/search" tabindex="-1"><button class="button">' + \
'<span>' + translate['Search'] + \
'</span></button></a>'
@ -245,7 +250,7 @@ def headerButtonsTimeline(defaultTimeline: str,
else:
tlStr += \
'<a href="' + usersPath + calendarPath + \
'"><button class="button">' + \
'" tabindex="-1"><button class="button">' + \
'<span>' + translate['Calendar'] + \
'</span></button></a>'
@ -262,13 +267,13 @@ def headerButtonsTimeline(defaultTimeline: str,
else:
tlStr += \
'<a href="' + usersPath + '/minimal' + \
'"><button class="button">' + \
'" tabindex="-1"><button class="button">' + \
'<span>' + translate['Show/Hide Buttons'] + \
'</span></button></a>'
if featuresHeader:
tlStr += \
'<a href="' + usersPath + '/inbox">' + \
'<a href="' + usersPath + '/inbox" tabindex="-1">' + \
'<button class="button">' + \
'<span>' + translate['User'] + '</span></button></a>'
@ -286,7 +291,7 @@ def headerButtonsTimeline(defaultTimeline: str,
tlStr += \
'<a href="' + \
usersPath + '/newswiremobile' + \
'"><button class="buttonMobile">' + \
'" tabindex="-1"><button class="buttonMobile">' + \
'<span>' + translate['Newswire'] + \
'</span></button></a>'
@ -304,13 +309,13 @@ def headerButtonsTimeline(defaultTimeline: str,
tlStr += \
'<a href="' + \
usersPath + '/linksmobile' + \
'"><button class="buttonMobile">' + \
'" tabindex="-1"><button class="buttonMobile">' + \
'<span>' + translate['Links'] + \
'</span></button></a>'
if featuresHeader:
tlStr += \
'<a href="' + usersPath + '/editprofile">' + \
'<a href="' + usersPath + '/editprofile" tabindex="-1">' + \
'<button class="buttonDesktop">' + \
'<span>' + translate['Settings'] + '</span></button></a>'

View File

@ -13,6 +13,8 @@ from utils import getConfigParam
from utils import noOfAccounts
from webapp_utils import htmlHeaderWithExternalStyle
from webapp_utils import htmlFooter
from webapp_utils import htmlKeyboardNavigation
from theme import getTextModeLogo
def htmlGetLoginCredentials(loginParams: str,
@ -75,6 +77,9 @@ def htmlLogin(cssCache: {}, translate: {},
loginImageFilename = baseDir + '/accounts/' + loginImage
copyfile(baseDir + '/img/login.png', loginImageFilename)
textModeLogo = getTextModeLogo(baseDir)
textModeLogoHtml = htmlKeyboardNavigation(textModeLogo, {})
if os.path.isfile(baseDir + '/accounts/login-background-custom.jpg'):
if not os.path.isfile(baseDir + '/accounts/login-background.jpg'):
copyfile(baseDir + '/accounts/login-background-custom.jpg',
@ -139,8 +144,7 @@ def htmlLogin(cssCache: {}, translate: {},
loginForm += '<form method="POST" action="/login">\n'
loginForm += ' <div class="imgcontainer">\n'
instanceTitle = getConfigParam(baseDir, 'instanceTitle')
if not instanceTitle:
instanceTitle = "Epicyon"
loginForm += textModeLogoHtml + '\n'
loginForm += \
' <img loading="lazy" src="' + loginImage + \
'" alt="' + instanceTitle + '" class="loginimage">\n'

View File

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

View File

@ -244,15 +244,19 @@ def _getAvatarImageHtml(showAvatarOptions: bool,
';' + str(pageNumber) + ';' + avatarUrl + messageIdStr + '">\n'
avatarLink += \
' <img loading="lazy" title="' + \
translate['Show options for this person'] + \
'" src="' + avatarUrl + '" ' + avatarPosition + \
translate['Show options for this person'] + '" ' + \
'alt="👤 ' + \
translate['Show options for this person'] + '" ' + \
'src="' + avatarUrl + '" ' + avatarPosition + \
getBrokenLinkSubstitute() + '/></a>\n'
else:
# don't link to the person options for the news account
avatarLink += \
' <img loading="lazy" title="' + \
translate['Show options for this person'] + \
'" src="' + avatarUrl + '" ' + avatarPosition + \
translate['Show options for this person'] + '" ' + \
'alt="👤 ' + \
translate['Show options for this person'] + '" ' + \
'src="' + avatarUrl + '" ' + avatarPosition + \
getBrokenLinkSubstitute() + '/>\n'
return avatarLink.strip()
@ -388,12 +392,14 @@ def _getAnnounceIconHtml(nickname: str, domainFull: str,
# don't allow announce/repeat of your own posts
announceIcon = 'repeat_inactive.png'
announceLink = 'repeat'
announceEmoji = ''
if not isPublicRepeat:
announceLink = 'repeatprivate'
announceTitle = translate['Repeat this post']
if announcedByPerson(postJsonObject, nickname, domainFull):
announceIcon = 'repeat.png'
announceEmoji = '🔁 '
if not isPublicRepeat:
announceLink = 'unrepeatprivate'
announceTitle = translate['Undo the repeat']
@ -409,7 +415,7 @@ def _getAnnounceIconHtml(nickname: str, domainFull: str,
announceStr += \
' ' + \
'<img loading="lazy" title="' + translate['Repeat this post'] + \
'" alt="' + translate['Repeat this post'] + \
'" alt="' + announceEmoji + translate['Repeat this post'] + \
' |" src="/icons/' + announceIcon + '"/></a>\n'
return announceStr
@ -430,6 +436,7 @@ def _getLikeIconHtml(nickname: str, domainFull: str,
likeIcon = 'like_inactive.png'
likeLink = 'like'
likeTitle = translate['Like this post']
likeEmoji = ''
likeCount = noOfLikes(postJsonObject)
_logPostTiming(enableTimingLog, postStartTime, '12.1')
@ -447,6 +454,7 @@ def _getLikeIconHtml(nickname: str, domainFull: str,
likeIcon = 'like.png'
likeLink = 'unlike'
likeTitle = translate['Undo the like']
likeEmoji = '👍 '
_logPostTiming(enableTimingLog, postStartTime, '12.2')
@ -467,7 +475,7 @@ def _getLikeIconHtml(nickname: str, domainFull: str,
likeStr += \
' ' + \
'<img loading="lazy" title="' + likeTitle + likeCountStr + \
'" alt="' + likeTitle + \
'" alt="' + likeEmoji + likeTitle + \
' |" src="/icons/' + likeIcon + '"/></a>\n'
return likeStr
@ -489,10 +497,12 @@ def _getBookmarkIconHtml(nickname: str, domainFull: str,
bookmarkIcon = 'bookmark_inactive.png'
bookmarkLink = 'bookmark'
bookmarkEmoji = ''
bookmarkTitle = translate['Bookmark this post']
if bookmarkedByPerson(postJsonObject, nickname, domainFull):
bookmarkIcon = 'bookmark.png'
bookmarkLink = 'unbookmark'
bookmarkEmoji = '🔖 '
bookmarkTitle = translate['Undo the bookmark']
_logPostTiming(enableTimingLog, postStartTime, '12.6')
bookmarkStr = \
@ -505,7 +515,7 @@ def _getBookmarkIconHtml(nickname: str, domainFull: str,
bookmarkStr += \
' ' + \
'<img loading="lazy" title="' + bookmarkTitle + '" alt="' + \
bookmarkTitle + ' |" src="/icons' + \
bookmarkEmoji + bookmarkTitle + ' |" src="/icons' + \
'/' + bookmarkIcon + '"/></a>\n'
return bookmarkStr
@ -548,7 +558,7 @@ def _getMuteIconHtml(isMuted: bool,
translate['Undo mute'] + '">\n'
muteStr += \
' ' + \
'<img loading="lazy" alt="' + translate['Undo mute'] + \
'<img loading="lazy" alt="🔇 ' + translate['Undo mute'] + \
' |" title="' + translate['Undo mute'] + \
'" src="/icons/unmute.png"/></a>\n'
return muteStr

View File

@ -40,6 +40,8 @@ from jami import getJamiAddress
from filters import isFiltered
from follow import isFollowerOfPerson
from webapp_frontscreen import htmlFrontScreen
from webapp_utils import htmlKeyboardNavigation
from webapp_utils import htmlHideFromScreenReader
from webapp_utils import scheduledPostsExist
from webapp_utils import getPersonAvatarUrl
from webapp_utils import htmlHeaderWithExternalStyle
@ -472,6 +474,7 @@ def htmlProfile(rssIconAtTop: bool,
newswire: {}, theme: str, dormantMonths: int,
peertubeInstances: [],
allowLocalNetworkAccess: bool,
textModeBanner: str,
extraJson=None, pageNumber=None,
maxItemsPerPage=None) -> str:
"""Show the profile page as html
@ -707,16 +710,37 @@ def htmlProfile(rssIconAtTop: bool,
movedTo, alsoKnownAs,
pinnedContent)
# Links for keyboard navigation
profileStr = \
'<div class="transparent">' + \
'<label class="transparent">' + \
'<a href="/users/' + nickname + '/' + defaultTimeline + '">' + \
translate['Switch to timeline view'] + '</a></label> | ' + \
'<label class="transparent">' + \
'<a class="skip-main" href="#buttonheader">' + \
translate['Skip to timeline'] + '</a></label>' + \
'</div>\n'
# keyboard navigation
userPathStr = '/users/' + nickname
deft = defaultTimeline
menuTimeline = \
htmlHideFromScreenReader('🏠') + ' ' + \
translate['Switch to timeline view']
menuEdit = \
htmlHideFromScreenReader('') + ' ' + translate['Edit']
menuFollowing = \
htmlHideFromScreenReader('👥') + ' ' + translate['Following']
menuFollowers = \
htmlHideFromScreenReader('👪') + ' ' + translate['Followers']
menuRoles = \
htmlHideFromScreenReader('🤚') + ' ' + translate['Roles']
menuSkills = \
htmlHideFromScreenReader('🛠') + ' ' + translate['Skills']
menuShares = \
htmlHideFromScreenReader('🤝') + ' ' + translate['Shares']
menuLogout = \
htmlHideFromScreenReader('') + ' ' + translate['Logout']
navLinks = {
menuTimeline: userPathStr + '/' + deft,
menuEdit: userPathStr + '/editprofile',
menuFollowing: userPathStr + '/following#timeline',
menuFollowers: userPathStr + '/followers#timeline',
menuRoles: userPathStr + '/roles#timeline',
menuSkills: userPathStr + '/skills#timeline',
menuShares: userPathStr + '/shares#timeline',
menuLogout: '/logout'
}
profileStr = htmlKeyboardNavigation(textModeBanner, navLinks)
profileStr += profileHeaderStr + donateSection
profileStr += '<div class="container" id="buttonheader">\n'
@ -750,6 +774,9 @@ def htmlProfile(rssIconAtTop: bool,
profileStr += ' </center>'
profileStr += '</div>'
# start of #timeline
profileStr += '<div id="timeline">\n'
profileStr += followApprovalsSection
cssFilename = baseDir + '/epicyon-profile.css'
@ -804,6 +831,8 @@ def htmlProfile(rssIconAtTop: bool,
_htmlProfileShares(actor, translate,
nickname, domainFull,
extraJson) + licenseStr
# end of #timeline
profileStr += '</div>'
instanceTitle = \
getConfigParam(baseDir, 'instanceTitle')
@ -985,7 +1014,8 @@ def _htmlProfileShares(actor: str, translate: {},
def htmlEditProfile(cssCache: {}, translate: {}, baseDir: str, path: str,
domain: str, port: int, httpPrefix: str,
defaultTimeline: str, theme: str,
peertubeInstances: []) -> str:
peertubeInstances: [],
textModeBanner: str) -> str:
"""Shows the edit profile screen
"""
imageFormats = getImageFormats()
@ -1332,13 +1362,26 @@ def htmlEditProfile(cssCache: {}, translate: {}, baseDir: str, path: str,
getConfigParam(baseDir, 'instanceTitle')
editProfileForm = htmlHeaderWithExternalStyle(cssFilename, instanceTitle)
# keyboard navigation
userPathStr = '/users/' + nickname
userTimalineStr = '/users/' + nickname + '/' + defaultTimeline
menuTimeline = \
htmlHideFromScreenReader('🏠') + ' ' + \
translate['Switch to timeline view']
menuProfile = \
htmlHideFromScreenReader('👤') + ' ' + \
translate['Switch to profile view']
navLinks = {
menuProfile: userPathStr,
menuTimeline: userTimalineStr
}
editProfileForm += htmlKeyboardNavigation(textModeBanner, navLinks)
# top banner
editProfileForm += \
'<a href="/users/' + nickname + '/' + defaultTimeline + '" title="' + \
translate['Switch to timeline view'] + '" alt="' + \
translate['Switch to timeline view'] + '">\n'
'<a href="/users/' + nickname + '/' + defaultTimeline + '">'
editProfileForm += '<img loading="lazy" class="timeline-banner" src="' + \
'/users/' + nickname + '/' + bannerFile + '" /></a>\n'
'/users/' + nickname + '/' + bannerFile + '" alt="" /></a>\n'
editProfileForm += \
'<form enctype="multipart/form-data" method="POST" ' + \

View File

@ -23,6 +23,7 @@ from utils import searchBoxPosts
from categories import getHashtagCategory
from feeds import rss2TagHeader
from feeds import rss2TagFooter
from webapp_utils import htmlKeyboardNavigation
from webapp_utils import getAltPath
from webapp_utils import htmlHeaderWithExternalStyle
from webapp_utils import htmlFooter
@ -317,7 +318,8 @@ def htmlSearchEmojiTextEntry(cssCache: {}, translate: {},
def htmlSearch(cssCache: {}, translate: {},
baseDir: str, path: str, domain: str,
defaultTimeline: str, theme: str) -> str:
defaultTimeline: str, theme: str,
textModeBanner: str) -> str:
"""Search called from the timeline icon
"""
actor = path.replace('/search', '')
@ -340,10 +342,14 @@ def htmlSearch(cssCache: {}, translate: {},
searchBannerFile, searchBannerFilename = \
getSearchBannerFile(baseDir, searchNickname, domain, theme)
textModeBannerStr = htmlKeyboardNavigation(textModeBanner, {})
if textModeBannerStr is None:
textModeBannerStr = ''
if os.path.isfile(searchBannerFilename):
usersPath = '/users/' + searchNickname
followStr += \
'<header>\n' + \
'<header>\n' + textModeBannerStr + \
'<a href="' + usersPath + '/' + defaultTimeline + '" title="' + \
translate['Switch to timeline view'] + '" alt="' + \
translate['Switch to timeline view'] + '">\n'

View File

@ -14,6 +14,8 @@ from utils import isEditor
from utils import removeIdEnding
from follow import followerApprovalActive
from person import isPersonSnoozed
from webapp_utils import htmlKeyboardNavigation
from webapp_utils import htmlHideFromScreenReader
from webapp_utils import htmlPostSeparator
from webapp_utils import getBannerFile
from webapp_utils import htmlHeaderWithExternalStyle
@ -64,7 +66,8 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
moderationActionStr: str,
theme: str,
peertubeInstances: [],
allowLocalNetworkAccess: bool) -> str:
allowLocalNetworkAccess: bool,
textModeBanner: str) -> str:
"""Show the timeline as html
"""
enableTimingLog = False
@ -279,14 +282,14 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
if not iconsAsButtons:
newPostButtonStr += \
'<a class="imageAnchor" href="' + usersPath + \
'/newdm"><img loading="lazy" src="/' + \
'/newdm?nodropdown"><img loading="lazy" src="/' + \
'icons/newpost.png" title="' + \
translate['Create a new DM'] + \
'" alt="| ' + translate['Create a new DM'] + \
'" class="timelineicon"/></a>\n'
else:
newPostButtonStr += \
'<a href="' + usersPath + '/newdm">' + \
'<a href="' + usersPath + '/newdm?nodropdown">' + \
'<button class="button"><span>' + \
translate['Post'] + ' </span></button></a>'
elif (boxName == 'tlblogs' or
@ -309,28 +312,28 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
if not iconsAsButtons:
newPostButtonStr += \
'<a class="imageAnchor" href="' + usersPath + \
'/newevent"><img loading="lazy" src="/' + \
'/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">' + \
'<a href="' + usersPath + '/newevent?nodropdown">' + \
'<button class="button"><span>' + \
translate['Post'] + '</span></button></a>'
elif boxName == 'tlshares':
if not iconsAsButtons:
newPostButtonStr += \
'<a class="imageAnchor" href="' + usersPath + \
'/newshare"><img loading="lazy" src="/' + \
'/newshare?nodropdown"><img loading="lazy" src="/' + \
'icons/newpost.png" title="' + \
translate['Create a new shared item'] + '" alt="| ' + \
translate['Create a new shared item'] + \
'" class="timelineicon"/></a>\n'
else:
newPostButtonStr += \
'<a href="' + usersPath + '/newshare">' + \
'<a href="' + usersPath + '/newshare?nodropdown">' + \
'<button class="button"><span>' + \
translate['Post'] + '</span></button></a>'
else:
@ -363,25 +366,74 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
'<button class="button"><span>' + \
translate['Post'] + '</span></button></a>'
# This creates a link to skip to the timeline and change to
# profile view when accessed within lynx, but should be
# invisible in a graphical web browser
tlStr += \
'<div class="transparent">' + \
'<label class="transparent">' + \
'<a href="/users/' + nickname + '">' + \
translate['Switch to profile view'] + '</a></label> | ' + \
'<label class="transparent">' + \
'<a class="skip-main" href="' + usersPath + '/' + boxName + \
'#timeline">' + \
translate['Skip to timeline'] + '</a></label> | ' + \
'<label class="transparent">' + \
'<a class="skip-newswire" href="#newswire">' + \
translate['Skip to Newswire'] + '</a></label> | ' + \
'<label class="transparent">' + \
'<a class="skip-links" href="#links">' + \
translate['Skip to Links'] + '</a></label>' + \
'</div>\n'
# keyboard navigation
calendarStr = translate['Calendar']
if newCalendarEvent:
calendarStr = '<strong>' + calendarStr + '</strong>'
dmStr = translate['DM']
if newDM:
dmStr = '<strong>' + dmStr + '</strong>'
repliesStr = translate['Replies']
if newReply:
repliesStr = '<strong>' + repliesStr + '</strong>'
sharesStr = translate['Shares']
if newShare:
sharesStr = '<strong>' + sharesStr + '</strong>'
menuProfile = \
htmlHideFromScreenReader('👤') + ' ' + \
translate['Switch to profile view']
menuInbox = \
htmlHideFromScreenReader('📥') + ' ' + translate['Inbox']
menuOutbox = \
htmlHideFromScreenReader('📤') + ' ' + translate['Outbox']
menuSearch = \
htmlHideFromScreenReader('🔍') + ' ' + \
translate['Search and follow']
menuCalendar = \
htmlHideFromScreenReader('📅') + ' ' + calendarStr
menuDM = \
htmlHideFromScreenReader('📩') + ' ' + dmStr
menuReplies = \
htmlHideFromScreenReader('📨') + ' ' + repliesStr
menuBookmarks = \
htmlHideFromScreenReader('🔖') + ' ' + \
translate['Bookmarks']
menuShares = \
htmlHideFromScreenReader('🤝') + ' ' + sharesStr
menuEvents = \
htmlHideFromScreenReader('🎫') + ' ' + translate['Events']
menuBlogs = \
htmlHideFromScreenReader('📝') + ' ' + translate['Blogs']
menuNewswire = \
htmlHideFromScreenReader('📰') + ' ' + translate['Newswire']
menuLinks = \
htmlHideFromScreenReader('🔗') + ' ' + translate['Links']
menuNewPost = \
htmlHideFromScreenReader('') + ' ' + \
translate['Create a new post']
menuModeration = \
htmlHideFromScreenReader('⚡️') + ' ' + \
translate['Mod']
navLinks = {
menuProfile: '/users/' + nickname,
menuInbox: usersPath + '/inbox#timeline',
menuSearch: usersPath + '/search',
menuNewPost: usersPath + '/newpost',
menuCalendar: usersPath + '/calendar',
menuDM: usersPath + '/dm#timeline',
menuReplies: usersPath + '/tlreplies#timeline',
menuOutbox: usersPath + '/inbox#timeline',
menuBookmarks: usersPath + '/tlbookmarks#timeline',
menuShares: usersPath + '/tlshares#timeline',
menuBlogs: usersPath + '/tlblogs#timeline',
menuEvents: usersPath + '/tlevents#timeline',
menuNewswire: '#newswire',
menuLinks: '#links'
}
if moderator:
navLinks[menuModeration] = usersPath + '/moderation#modtimeline'
tlStr += htmlKeyboardNavigation(textModeBanner, navLinks,
usersPath, translate, followApprovals)
# banner and row of buttons
tlStr += \
@ -433,7 +485,6 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
leftColumnStr + ' </td>\n'
# center column containing posts
tlStr += ' <td valign="top" class="col-center">\n'
tlStr += ' <main id="timeline" tabindex="-1">\n'
if not fullWidthTimelineButtonHeader:
tlStr += \
@ -451,12 +502,12 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
calendarImage, followApprovals,
iconsAsButtons)
tlStr += ' <div class="timeline-posts">\n'
tlStr += ' <div id="timeline" class="timeline-posts">\n'
# second row of buttons for moderator actions
if moderator and boxName == 'moderation':
tlStr += \
'<form method="POST" action="/users/' + \
'<form id="modtimeline" method="POST" action="/users/' + \
nickname + '/moderationaction">'
tlStr += '<div class="container">\n'
idx = 'Nickname or URL. Block using *@domain or nickname@domain'
@ -473,40 +524,57 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
tlStr += \
' <input type="submit" title="' + \
translate['Information about current blocks/suspensions'] + \
'" name="submitInfo" value="' + translate['Info'] + '">\n'
'" alt="' + \
translate['Information about current blocks/suspensions'] + \
' | " ' + \
'name="submitInfo" value="' + translate['Info'] + '">\n'
tlStr += \
' <input type="submit" title="' + \
translate['Remove the above item'] + \
'" name="submitRemove" value="' + \
translate['Remove the above item'] + '" ' + \
'alt="' + translate['Remove the above item'] + ' | " ' + \
'name="submitRemove" value="' + \
translate['Remove'] + '">\n'
tlStr += \
' <input type="submit" title="' + \
translate['Suspend the above account nickname'] + \
'" name="submitSuspend" value="' + translate['Suspend'] + '">\n'
translate['Suspend the above account nickname'] + '" ' + \
'alt="' + \
translate['Suspend the above account nickname'] + ' | " ' + \
'name="submitSuspend" value="' + translate['Suspend'] + '">\n'
tlStr += \
' <input type="submit" title="' + \
translate['Remove a suspension for an account nickname'] + '" ' + \
'alt="' + \
translate['Remove a suspension for an account nickname'] + \
'" name="submitUnsuspend" value="' + \
' | " ' + \
'name="submitUnsuspend" value="' + \
translate['Unsuspend'] + '">\n'
tlStr += \
' <input type="submit" title="' + \
translate['Block an account on another instance'] + \
'" name="submitBlock" value="' + translate['Block'] + '">\n'
translate['Block an account on another instance'] + '" ' + \
'alt="' + \
translate['Block an account on another instance'] + ' | " ' + \
'name="submitBlock" value="' + translate['Block'] + '">\n'
tlStr += \
' <input type="submit" title="' + \
translate['Unblock an account on another instance'] + \
'" name="submitUnblock" value="' + translate['Unblock'] + '">\n'
translate['Unblock an account on another instance'] + '" ' + \
'alt="' + \
translate['Unblock an account on another instance'] + ' | " ' + \
'name="submitUnblock" value="' + translate['Unblock'] + '">\n'
tlStr += \
' <input type="submit" title="' + \
translate['Filter out words'] + \
'" name="submitFilter" value="' + translate['Filter'] + '">\n'
translate['Filter out words'] + '" ' + \
'alt="' + \
translate['Filter out words'] + ' | " ' + \
'name="submitFilter" value="' + translate['Filter'] + '">\n'
tlStr += \
' <input type="submit" title="' + \
translate['Unfilter words'] + \
'" name="submitUnfilter" value="' + translate['Unfilter'] + '">\n'
translate['Unfilter words'] + '" ' + \
'alt="' + \
translate['Unfilter words'] + ' | " ' + \
'name="submitUnfilter" value="' + translate['Unfilter'] + '">\n'
tlStr += '</div>\n</form>\n'
@ -522,8 +590,14 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
_logTimelineTiming(enableTimingLog, timelineStartTime, boxName, '7')
# separator between posts which only appears in shell browsers
# such as Lynx and is not read by screen readers
textModeSeparator = \
'<div class="transparent"><hr></div>'
# page up arrow
if pageNumber > 1:
tlStr += textModeSeparator
tlStr += \
' <center>\n' + \
' <a href="' + usersPath + '/' + boxName + \
@ -602,7 +676,7 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
if currTlStr:
itemCtr += 1
tlStr += currTlStr
tlStr += textModeSeparator + currTlStr
if separatorStr:
tlStr += separatorStr
if boxName == 'tlmedia':
@ -610,6 +684,7 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
# page down arrow
if itemCtr > 2:
tlStr += textModeSeparator
tlStr += \
' <center>\n' + \
' <a href="' + usersPath + '/' + boxName + '?page=' + \
@ -619,6 +694,7 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
translate['Page down'] + '" alt="' + \
translate['Page down'] + '"></a>\n' + \
' </center>\n'
tlStr += textModeSeparator
# end of timeline-posts
tlStr += ' </div>\n'
@ -634,8 +710,8 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
False, None, True,
showPublishAsIcon,
rssIconAtTop, publishButtonAtTop,
authorized, True, theme)
tlStr += ' </main>\n'
authorized, True, theme,
defaultTimeline)
tlStr += ' <td valign="top" class="col-right" ' + \
'id="newswire" tabindex="-1">' + \
rightColumnStr + ' </td>\n'
@ -752,7 +828,8 @@ def htmlShares(cssCache: {}, defaultTimeline: str,
publishButtonAtTop: bool,
authorized: bool, theme: str,
peertubeInstances: [],
allowLocalNetworkAccess: bool) -> str:
allowLocalNetworkAccess: bool,
textModeBanner: str) -> str:
"""Show the shares timeline as html
"""
manuallyApproveFollowers = \
@ -773,7 +850,7 @@ def htmlShares(cssCache: {}, defaultTimeline: str,
fullWidthTimelineButtonHeader,
iconsAsButtons, rssIconAtTop, publishButtonAtTop,
authorized, None, theme, peertubeInstances,
allowLocalNetworkAccess)
allowLocalNetworkAccess, textModeBanner)
def htmlInbox(cssCache: {}, defaultTimeline: str,
@ -794,7 +871,8 @@ def htmlInbox(cssCache: {}, defaultTimeline: str,
publishButtonAtTop: bool,
authorized: bool, theme: str,
peertubeInstances: [],
allowLocalNetworkAccess: bool) -> str:
allowLocalNetworkAccess: bool,
textModeBanner: str) -> str:
"""Show the inbox as html
"""
manuallyApproveFollowers = \
@ -815,7 +893,7 @@ def htmlInbox(cssCache: {}, defaultTimeline: str,
fullWidthTimelineButtonHeader,
iconsAsButtons, rssIconAtTop, publishButtonAtTop,
authorized, None, theme, peertubeInstances,
allowLocalNetworkAccess)
allowLocalNetworkAccess, textModeBanner)
def htmlBookmarks(cssCache: {}, defaultTimeline: str,
@ -836,7 +914,8 @@ def htmlBookmarks(cssCache: {}, defaultTimeline: str,
publishButtonAtTop: bool,
authorized: bool, theme: str,
peertubeInstances: [],
allowLocalNetworkAccess: bool) -> str:
allowLocalNetworkAccess: bool,
textModeBanner: str) -> str:
"""Show the bookmarks as html
"""
manuallyApproveFollowers = \
@ -857,7 +936,7 @@ def htmlBookmarks(cssCache: {}, defaultTimeline: str,
fullWidthTimelineButtonHeader,
iconsAsButtons, rssIconAtTop, publishButtonAtTop,
authorized, None, theme, peertubeInstances,
allowLocalNetworkAccess)
allowLocalNetworkAccess, textModeBanner)
def htmlEvents(cssCache: {}, defaultTimeline: str,
@ -878,7 +957,8 @@ def htmlEvents(cssCache: {}, defaultTimeline: str,
publishButtonAtTop: bool,
authorized: bool, theme: str,
peertubeInstances: [],
allowLocalNetworkAccess: bool) -> str:
allowLocalNetworkAccess: bool,
textModeBanner: str) -> str:
"""Show the events as html
"""
manuallyApproveFollowers = \
@ -899,7 +979,7 @@ def htmlEvents(cssCache: {}, defaultTimeline: str,
fullWidthTimelineButtonHeader,
iconsAsButtons, rssIconAtTop, publishButtonAtTop,
authorized, None, theme, peertubeInstances,
allowLocalNetworkAccess)
allowLocalNetworkAccess, textModeBanner)
def htmlInboxDMs(cssCache: {}, defaultTimeline: str,
@ -920,7 +1000,8 @@ def htmlInboxDMs(cssCache: {}, defaultTimeline: str,
publishButtonAtTop: bool,
authorized: bool, theme: str,
peertubeInstances: [],
allowLocalNetworkAccess: bool) -> str:
allowLocalNetworkAccess: bool,
textModeBanner: str) -> str:
"""Show the DM timeline as html
"""
return htmlTimeline(cssCache, defaultTimeline,
@ -936,7 +1017,7 @@ def htmlInboxDMs(cssCache: {}, defaultTimeline: str,
fullWidthTimelineButtonHeader,
iconsAsButtons, rssIconAtTop, publishButtonAtTop,
authorized, None, theme, peertubeInstances,
allowLocalNetworkAccess)
allowLocalNetworkAccess, textModeBanner)
def htmlInboxReplies(cssCache: {}, defaultTimeline: str,
@ -957,7 +1038,8 @@ def htmlInboxReplies(cssCache: {}, defaultTimeline: str,
publishButtonAtTop: bool,
authorized: bool, theme: str,
peertubeInstances: [],
allowLocalNetworkAccess: bool) -> str:
allowLocalNetworkAccess: bool,
textModeBanner: str) -> str:
"""Show the replies timeline as html
"""
return htmlTimeline(cssCache, defaultTimeline,
@ -974,7 +1056,7 @@ def htmlInboxReplies(cssCache: {}, defaultTimeline: str,
fullWidthTimelineButtonHeader,
iconsAsButtons, rssIconAtTop, publishButtonAtTop,
authorized, None, theme, peertubeInstances,
allowLocalNetworkAccess)
allowLocalNetworkAccess, textModeBanner)
def htmlInboxMedia(cssCache: {}, defaultTimeline: str,
@ -995,7 +1077,8 @@ def htmlInboxMedia(cssCache: {}, defaultTimeline: str,
publishButtonAtTop: bool,
authorized: bool, theme: str,
peertubeInstances: [],
allowLocalNetworkAccess: bool) -> str:
allowLocalNetworkAccess: bool,
textModeBanner: str) -> str:
"""Show the media timeline as html
"""
return htmlTimeline(cssCache, defaultTimeline,
@ -1012,7 +1095,7 @@ def htmlInboxMedia(cssCache: {}, defaultTimeline: str,
fullWidthTimelineButtonHeader,
iconsAsButtons, rssIconAtTop, publishButtonAtTop,
authorized, None, theme, peertubeInstances,
allowLocalNetworkAccess)
allowLocalNetworkAccess, textModeBanner)
def htmlInboxBlogs(cssCache: {}, defaultTimeline: str,
@ -1033,7 +1116,8 @@ def htmlInboxBlogs(cssCache: {}, defaultTimeline: str,
publishButtonAtTop: bool,
authorized: bool, theme: str,
peertubeInstances: [],
allowLocalNetworkAccess: bool) -> str:
allowLocalNetworkAccess: bool,
textModeBanner: str) -> str:
"""Show the blogs timeline as html
"""
return htmlTimeline(cssCache, defaultTimeline,
@ -1050,7 +1134,7 @@ def htmlInboxBlogs(cssCache: {}, defaultTimeline: str,
fullWidthTimelineButtonHeader,
iconsAsButtons, rssIconAtTop, publishButtonAtTop,
authorized, None, theme, peertubeInstances,
allowLocalNetworkAccess)
allowLocalNetworkAccess, textModeBanner)
def htmlInboxFeatures(cssCache: {}, defaultTimeline: str,
@ -1072,7 +1156,8 @@ def htmlInboxFeatures(cssCache: {}, defaultTimeline: str,
authorized: bool,
theme: str,
peertubeInstances: [],
allowLocalNetworkAccess: bool) -> str:
allowLocalNetworkAccess: bool,
textModeBanner: str) -> str:
"""Show the features timeline as html
"""
return htmlTimeline(cssCache, defaultTimeline,
@ -1089,7 +1174,7 @@ def htmlInboxFeatures(cssCache: {}, defaultTimeline: str,
fullWidthTimelineButtonHeader,
iconsAsButtons, rssIconAtTop, publishButtonAtTop,
authorized, None, theme, peertubeInstances,
allowLocalNetworkAccess)
allowLocalNetworkAccess, textModeBanner)
def htmlInboxNews(cssCache: {}, defaultTimeline: str,
@ -1110,7 +1195,8 @@ def htmlInboxNews(cssCache: {}, defaultTimeline: str,
publishButtonAtTop: bool,
authorized: bool, theme: str,
peertubeInstances: [],
allowLocalNetworkAccess: bool) -> str:
allowLocalNetworkAccess: bool,
textModeBanner: str) -> str:
"""Show the news timeline as html
"""
return htmlTimeline(cssCache, defaultTimeline,
@ -1127,7 +1213,7 @@ def htmlInboxNews(cssCache: {}, defaultTimeline: str,
fullWidthTimelineButtonHeader,
iconsAsButtons, rssIconAtTop, publishButtonAtTop,
authorized, None, theme, peertubeInstances,
allowLocalNetworkAccess)
allowLocalNetworkAccess, textModeBanner)
def htmlOutbox(cssCache: {}, defaultTimeline: str,
@ -1148,7 +1234,8 @@ def htmlOutbox(cssCache: {}, defaultTimeline: str,
publishButtonAtTop: bool,
authorized: bool, theme: str,
peertubeInstances: [],
allowLocalNetworkAccess: bool) -> str:
allowLocalNetworkAccess: bool,
textModeBanner: str) -> str:
"""Show the Outbox as html
"""
manuallyApproveFollowers = \
@ -1166,4 +1253,4 @@ def htmlOutbox(cssCache: {}, defaultTimeline: str,
showPublishAsIcon, fullWidthTimelineButtonHeader,
iconsAsButtons, rssIconAtTop, publishButtonAtTop,
authorized, None, theme, peertubeInstances,
allowLocalNetworkAccess)
allowLocalNetworkAccess, textModeBanner)

View File

@ -878,3 +878,35 @@ def getAvatarImageUrl(session,
avatarUrl = postActor + '/avatar.png'
return avatarUrl
def htmlHideFromScreenReader(htmlStr: str) -> str:
"""Returns html which is hidden from screen readers
"""
return '<span aria-hidden="true">' + htmlStr + '</span>'
def htmlKeyboardNavigation(banner: str, links: {},
usersPath=None, translate=None,
followApprovals=False) -> str:
"""Given a set of links return the html for keyboard navigation
"""
htmlStr = '<div class="transparent"><ul>'
if banner:
htmlStr += '<pre aria-label="">' + banner + '<br><br></pre>'
# show new follower approvals
if usersPath and translate and followApprovals:
htmlStr += '<strong><label class="transparent">' + \
'<a href="' + usersPath + '/followers#timeline">' + \
translate['Approve follow requests'] + '</a>' + \
'</label></strong><br><br>'
# show the list of links
for title, url in links.items():
htmlStr += '<li><label class="transparent">' + \
'<a href="' + str(url) + '">' + \
str(title) + '</a></label></li>'
htmlStr += '</ul></div>'
return htmlStr