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

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

View File

@ -94,6 +94,14 @@ form {
border-radius: var(--form-border-radius); 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] { input[type=text], input[type=password] {
width: 100%; width: 100%;
padding: 12px 20px; padding: 12px 20px;

View File

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

View File

@ -102,6 +102,14 @@ a:focus {
border: 2px solid var(--focus-color); border: 2px solid var(--focus-color);
} }
.transparent {
color: transparent;
background: transparent;
font-size: 0px;
line-height: 0px;
height: 0px;
}
.cw { .cw {
font-style: var(--cw-style); font-style: var(--cw-style);
color: var(--cw-color); 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/i2p
deploy/onion deploy/onion
install_requires = install_requires =
crypto >= 1.4.1, < 2
idna >= 2.5, < 3 idna >= 2.5, < 3
numpy >= 1.20.0, < 2 numpy >= 1.20.0, < 2
pillow >= 8.1.0, < 9 pillow >= 8.1.0, < 9
cryptography cryptography >= 3.3.1, < 4
pyqrcode >= 1.2.1, < 2 pyqrcode >= 1.2.1, < 2
python-dateutil >= 2.8.1, < 3 python-dateutil >= 2.8.1, < 3
requests >= 2.25.1, < 3 requests >= 2.25.1, < 3

View File

@ -15,6 +15,8 @@ from content import dangerousCSS
def _getThemeFiles() -> []: def _getThemeFiles() -> []:
"""Gets the list of theme style sheets
"""
return ('epicyon.css', 'login.css', 'follow.css', return ('epicyon.css', 'login.css', 'follow.css',
'suspended.css', 'calendar.css', 'blog.css', 'suspended.css', 'calendar.css', 'blog.css',
'options.css', 'search.css', 'links.css') 'options.css', 'search.css', 'links.css')
@ -39,6 +41,8 @@ def getThemesList(baseDir: str) -> []:
def _setThemeInConfig(baseDir: str, name: str) -> bool: def _setThemeInConfig(baseDir: str, name: str) -> bool:
"""Sets the theme with the given name within config.json
"""
configFilename = baseDir + '/config.json' configFilename = baseDir + '/config.json'
if not os.path.isfile(configFilename): if not os.path.isfile(configFilename):
return False return False
@ -118,6 +122,8 @@ def _setFullWidthTimelineButtonHeader(baseDir: str, fullWidth: bool) -> bool:
def getTheme(baseDir: str) -> str: def getTheme(baseDir: str) -> str:
"""Gets the current theme name from config.json
"""
configFilename = baseDir + '/config.json' configFilename = baseDir + '/config.json'
if os.path.isfile(configFilename): if os.path.isfile(configFilename):
configJson = loadJson(configFilename, 0) configJson = loadJson(configFilename, 0)
@ -128,6 +134,8 @@ def getTheme(baseDir: str) -> str:
def _removeTheme(baseDir: str): def _removeTheme(baseDir: str):
"""Removes the current theme style sheets
"""
themeFiles = _getThemeFiles() themeFiles = _getThemeFiles()
for filename in themeFiles: for filename in themeFiles:
if os.path.isfile(baseDir + '/' + filename): if os.path.isfile(baseDir + '/' + filename):
@ -422,6 +430,63 @@ def _setThemeFonts(baseDir: str, themeName: str) -> None:
break 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: def _setThemeImages(baseDir: str, name: str) -> None:
"""Changes the profile background image """Changes the profile background image
and banner to the defaults and banner to the defaults
@ -439,6 +504,8 @@ def _setThemeImages(baseDir: str, name: str) -> None:
rightColImageFilename = \ rightColImageFilename = \
baseDir + '/theme/' + themeNameLower + '/right_col_image.png' baseDir + '/theme/' + themeNameLower + '/right_col_image.png'
_setTextModeTheme(baseDir, themeNameLower)
backgroundNames = ('login', 'shares', 'delete', 'follow', backgroundNames = ('login', 'shares', 'delete', 'follow',
'options', 'block', 'search', 'calendar') 'options', 'block', 'search', 'calendar')
extensions = getImageExtensions() extensions = getImageExtensions()
@ -554,6 +621,8 @@ def setNewsAvatar(baseDir: str, name: str,
def setTheme(baseDir: str, name: str, domain: str, def setTheme(baseDir: str, name: str, domain: str,
allowLocalNetworkAccess: bool) -> bool: allowLocalNetworkAccess: bool) -> bool:
"""Sets the theme with the given name as the current theme
"""
result = False result = False
prevThemeName = getTheme(baseDir) 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": "الإصدار", "Version": "الإصدار",
"Skip to timeline": "تخطي إلى الجدول الزمني", "Skip to timeline": "تخطي إلى الجدول الزمني",
"Skip to Newswire": "انتقل إلى Newswire", "Skip to Newswire": "انتقل إلى Newswire",
"Skip to Links": "تخطي إلى روابط الويب" "Skip to Links": "تخطي إلى روابط الويب",
"Publish a blog article": "نشر مقال بلوق"
} }

View File

@ -366,5 +366,6 @@
"Version": "Versió", "Version": "Versió",
"Skip to timeline": "Ves a la cronologia", "Skip to timeline": "Ves a la cronologia",
"Skip to Newswire": "Vés a Newswire", "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", "Version": "Fersiwn",
"Skip to timeline": "Neidio i'r llinell amser", "Skip to timeline": "Neidio i'r llinell amser",
"Skip to Newswire": "Neidio i Newswire", "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", "Version": "Ausführung",
"Skip to timeline": "Zur Zeitleiste springen", "Skip to timeline": "Zur Zeitleiste springen",
"Skip to Newswire": "Springe zu Newswire", "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", "Version": "Version",
"Skip to timeline": "Skip to timeline", "Skip to timeline": "Skip to timeline",
"Skip to Newswire": "Skip to Newswire", "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", "Version": "Versión",
"Skip to timeline": "Saltar a la línea de tiempo", "Skip to timeline": "Saltar a la línea de tiempo",
"Skip to Newswire": "Saltar a Newswire", "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", "Version": "Version",
"Skip to timeline": "Passer à la chronologie", "Skip to timeline": "Passer à la chronologie",
"Skip to Newswire": "Passer à Newswire", "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", "Version": "Leagan",
"Skip to timeline": "Scipeáil chuig an amlíne", "Skip to timeline": "Scipeáil chuig an amlíne",
"Skip to Newswire": "Scipeáil chuig Newswire", "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": "संस्करण", "Version": "संस्करण",
"Skip to timeline": "टाइमलाइन पर जाएं", "Skip to timeline": "टाइमलाइन पर जाएं",
"Skip to Newswire": "Newswire पर जाएं", "Skip to Newswire": "Newswire पर जाएं",
"Skip to Links": "वेब लिंक पर जाएं" "Skip to Links": "वेब लिंक पर जाएं",
"Publish a blog article": "एक ब्लॉग लेख प्रकाशित करें"
} }

View File

@ -366,5 +366,6 @@
"Version": "Versione", "Version": "Versione",
"Skip to timeline": "Passa alla sequenza temporale", "Skip to timeline": "Passa alla sequenza temporale",
"Skip to Newswire": "Passa a Newswire", "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": "バージョン", "Version": "バージョン",
"Skip to timeline": "タイムラインにスキップ", "Skip to timeline": "タイムラインにスキップ",
"Skip to Newswire": "Newswireにスキップ", "Skip to Newswire": "Newswireにスキップ",
"Skip to Links": "Webリンクにスキップ" "Skip to Links": "Webリンクにスキップ",
"Publish a blog article": "ブログ記事を公開する"
} }

View File

@ -362,5 +362,6 @@
"Version": "Version", "Version": "Version",
"Skip to timeline": "Skip to timeline", "Skip to timeline": "Skip to timeline",
"Skip to Newswire": "Skip to Newswire", "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", "Version": "Versão",
"Skip to timeline": "Pular para a linha do tempo", "Skip to timeline": "Pular para a linha do tempo",
"Skip to Newswire": "Pular para Newswire", "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": "Версия", "Version": "Версия",
"Skip to timeline": "Перейти к временной шкале", "Skip to timeline": "Перейти к временной шкале",
"Skip to Newswire": "Перейти к ленте новостей", "Skip to Newswire": "Перейти к ленте новостей",
"Skip to Links": "Перейти к веб-ссылкам" "Skip to Links": "Перейти к веб-ссылкам",
"Publish a blog article": "Опубликовать статью в блоге"
} }

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": "跳到网页链接",
"Publish a blog article": "发布博客文章"
} }

View File

@ -494,10 +494,20 @@ def evilIncarnate() -> []:
def isEvil(domain: str) -> bool: def isEvil(domain: str) -> bool:
# https://www.youtube.com/watch?v=5qw1hcevmdU
if not isinstance(domain, str): if not isinstance(domain, str):
print('WARN: Malformed domain ' + str(domain)) print('WARN: Malformed domain ' + str(domain))
return True 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() evilDomains = evilIncarnate()
for concentratedEvil in evilDomains: for concentratedEvil in evilDomains:
if domain.endswith(concentratedEvil): if domain.endswith(concentratedEvil):

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -878,3 +878,35 @@ def getAvatarImageUrl(session,
avatarUrl = postActor + '/avatar.png' avatarUrl = postActor + '/avatar.png'
return avatarUrl 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