Merge branch 'main' of gitlab.com:bashrc2/epicyon

merge-requests/30/head
Bob Mottram 2022-05-14 14:31:03 +01:00
commit 29601b2346
69 changed files with 340 additions and 52 deletions

View File

@ -617,6 +617,17 @@ class PubServer(BaseHTTPRequestHandler):
return True
return False
def _request_ssml(self) -> bool:
"""Should a ssml response be given?
"""
if not self.headers.get('Accept'):
return False
accept_str = self.headers['Accept']
if 'application/ssml' in accept_str:
if 'text/html' not in accept_str:
return True
return False
def _request_http(self) -> bool:
"""Should a http response be given?
"""
@ -11025,7 +11036,7 @@ class PubServer(BaseHTTPRequestHandler):
self._redirect_headers(actor_absolute, cookie, calling_domain)
return True
def _show_individual_at_post(self, authorized: bool,
def _show_individual_at_post(self, ssml_getreq: bool, authorized: bool,
calling_domain: str, referer_domain: str,
path: str,
base_dir: str, http_prefix: str,
@ -11073,6 +11084,35 @@ class PubServer(BaseHTTPRequestHandler):
if len(status_number) <= 10 or not status_number.isdigit():
return False
if ssml_getreq:
ssml_filename = \
acct_dir(base_dir, nickname, domain) + '/outbox/' + \
http_prefix + ':##' + domain_full + '#users#' + nickname + \
'#statuses#' + status_number + '.ssml'
if not os.path.isfile(ssml_filename):
ssml_filename = \
acct_dir(base_dir, nickname, domain) + '/postcache/' + \
http_prefix + ':##' + domain_full + '#users#' + \
nickname + '#statuses#' + status_number + '.ssml'
if not os.path.isfile(ssml_filename):
self._404()
return True
ssml_str = None
try:
with open(ssml_filename, 'r') as fp_ssml:
ssml_str = fp_ssml.read()
except OSError:
pass
if ssml_str:
msg = ssml_str.encode('utf-8')
msglen = len(msg)
self._set_headers('application/ssml+xml', msglen,
cookie, calling_domain, False)
self._write(msg)
return True
self._404()
return True
post_filename = \
acct_dir(base_dir, nickname, domain) + '/outbox/' + \
http_prefix + ':##' + domain_full + '#users#' + nickname + \
@ -11346,7 +11386,7 @@ class PubServer(BaseHTTPRequestHandler):
self.server.getreq_busy = False
return True
def _show_individual_post(self, authorized: bool,
def _show_individual_post(self, ssml_getreq: bool, authorized: bool,
calling_domain: str, referer_domain: str,
path: str,
base_dir: str, http_prefix: str,
@ -11388,6 +11428,35 @@ class PubServer(BaseHTTPRequestHandler):
if len(status_number) <= 10 or (not status_number.isdigit()):
return False
if ssml_getreq:
ssml_filename = \
acct_dir(base_dir, nickname, domain) + '/outbox/' + \
http_prefix + ':##' + domain_full + '#users#' + nickname + \
'#statuses#' + status_number + '.ssml'
if not os.path.isfile(ssml_filename):
ssml_filename = \
acct_dir(base_dir, nickname, domain) + '/postcache/' + \
http_prefix + ':##' + domain_full + '#users#' + \
nickname + '#statuses#' + status_number + '.ssml'
if not os.path.isfile(ssml_filename):
self._404()
return True
ssml_str = None
try:
with open(ssml_filename, 'r') as fp_ssml:
ssml_str = fp_ssml.read()
except OSError:
pass
if ssml_str:
msg = ssml_str.encode('utf-8')
msglen = len(msg)
self._set_headers('application/ssml+xml', msglen,
cookie, calling_domain, False)
self._write(msg)
return True
self._404()
return True
post_filename = \
acct_dir(base_dir, nickname, domain) + '/outbox/' + \
http_prefix + ':##' + domain_full + '#users#' + nickname + \
@ -15387,12 +15456,15 @@ class PubServer(BaseHTTPRequestHandler):
'_GET', 'create session',
self.server.debug)
# is this a html request?
# is this a html/ssml/icalendar request?
html_getreq = False
ssml_getreq = False
icalendar_getreq = False
if self._has_accept(calling_domain):
if self._request_http():
html_getreq = True
elif self._request_ssml():
ssml_getreq = True
elif self._request_icalendar():
icalendar_getreq = True
else:
@ -16184,7 +16256,8 @@ class PubServer(BaseHTTPRequestHandler):
self.server.translate,
access_keys,
self.server.access_keys,
self.server.default_timeline)
self.server.default_timeline,
self.server.theme_name)
msg = msg.encode('utf-8')
msglen = len(msg)
self._login_headers('text/html', msglen, calling_domain)
@ -17628,7 +17701,7 @@ class PubServer(BaseHTTPRequestHandler):
self.server.debug)
# get an individual post from the path /@nickname/statusnumber
if self._show_individual_at_post(authorized,
if self._show_individual_at_post(ssml_getreq, authorized,
calling_domain, referer_domain,
self.path,
self.server.base_dir,
@ -17773,7 +17846,7 @@ class PubServer(BaseHTTPRequestHandler):
# get an individual post from the path
# /users/nickname/statuses/number
if '/statuses/' in self.path and users_in_path:
if self._show_individual_post(authorized,
if self._show_individual_post(ssml_getreq, authorized,
calling_domain, referer_domain,
self.path,
self.server.base_dir,

View File

@ -46,6 +46,7 @@ from session import create_session
from session import get_json
from session import get_vcard
from session import download_html
from session import download_ssml
from newswire import get_rss
from filters import add_filter
from filters import remove_filter
@ -341,6 +342,8 @@ parser.add_argument('--xmlvcard', dest='xmlvcard', type=str, default=None,
'activitypub actor url')
parser.add_argument('--json', dest='json', type=str, default=None,
help='Show the json for a given activitypub url')
parser.add_argument('--ssml', dest='ssml', type=str, default=None,
help='Show the SSML for a given activitypub url')
parser.add_argument('--htmlpost', dest='htmlpost', type=str, default=None,
help='Show the html for a given activitypub url')
parser.add_argument('--rss', dest='rss', type=str, default=None,
@ -1040,6 +1043,31 @@ if args.json:
pprint(test_json)
sys.exit()
if args.ssml:
session = create_session(None)
profile_str = 'https://www.w3.org/ns/activitystreams'
as_header = {
'Accept': 'application/ssml+xml; profile="' + profile_str + '"'
}
if not args.domain:
args.domain = get_config_param(base_dir, 'domain')
domain = ''
if args.domain:
domain = args.domain
signing_priv_key_pem = get_instance_actor_key(base_dir, domain)
if debug:
print('base_dir: ' + str(base_dir))
if signing_priv_key_pem:
print('Obtained instance actor signing key')
else:
print('Did not obtain instance actor key for ' + domain)
test_ssml = download_ssml(signing_priv_key_pem, session, args.ssml,
as_header, None, debug, __version__,
http_prefix, domain)
if test_ssml:
print(str(test_ssml))
sys.exit()
if args.vcard:
session = create_session(None)
if not args.domain:
@ -1084,11 +1112,11 @@ if args.htmlpost:
print('Obtained instance actor signing key')
else:
print('Did not obtain instance actor key for ' + domain)
testHtml = download_html(signing_priv_key_pem, session, args.htmlpost,
as_header, None, debug, __version__,
http_prefix, domain)
if testHtml:
print(testHtml)
test_html = download_html(signing_priv_key_pem, session, args.htmlpost,
as_header, None, debug, __version__,
http_prefix, domain)
if test_html:
print(test_html)
sys.exit()
# create cache for actors

View File

@ -2282,7 +2282,8 @@ def _receive_announce(recent_posts_cache: {},
nickname, domain, domain_full,
post_json_object, person_cache,
translate, lookup_actor,
theme_name)
theme_name, system_language,
'inbox')
try:
with open(post_filename + '.tts', 'w+') as ttsfile:
ttsfile.write('\n')
@ -4120,7 +4121,8 @@ def _inbox_after_initial(server, inbox_start_time,
update_speaker(base_dir, http_prefix,
nickname, domain, domain_full,
post_json_object, person_cache,
translate, None, theme_name)
translate, None, theme_name,
system_language, boxname)
fitness_performance(inbox_start_time,
server.fitness,
'INBOX', 'update_speaker',

View File

@ -57,6 +57,7 @@ from delete import outbox_delete
from shares import outbox_share_upload
from shares import outbox_undo_share_upload
from webapp_post import individual_post_as_html
from speaker import update_speaker
def _person_receive_update_outbox(recent_posts_cache: {},
@ -400,6 +401,13 @@ def post_message_to_outbox(session, translate: {},
print('WARN: post not saved to outbox ' + outbox_name)
return False
update_speaker(base_dir, http_prefix,
post_to_nickname, domain, domain_full,
message_json, person_cache,
translate, message_json['actor'],
theme, system_language,
outbox_name)
# save all instance blogs to the news actor
if post_to_nickname != 'news' and outbox_name == 'tlblogs':
if '/' in saved_filename:

View File

@ -373,6 +373,45 @@ def download_html(signing_priv_key_pem: str,
None, quiet, debug, False)
def download_ssml(signing_priv_key_pem: str,
session, url: str, headers: {}, params: {}, debug: bool,
version: str = __version__, http_prefix: str = 'https',
domain: str = 'testdomain',
timeout_sec: int = 20, quiet: bool = False) -> {}:
if not isinstance(url, str):
if debug and not quiet:
print('url: ' + str(url))
print('ERROR: download_ssml failed, url should be a string')
return None
session_params = {}
session_headers = {}
if headers:
session_headers = headers
if params:
session_params = params
session_headers['Accept'] = 'application/ssml+xml'
session_headers['User-Agent'] = 'Epicyon/' + version
if domain:
session_headers['User-Agent'] += \
'; +' + http_prefix + '://' + domain + '/'
if not session:
if not quiet:
print('WARN: download_ssml failed, no session specified')
return None
if debug:
HTTPConnection.debuglevel = 1
if signing_priv_key_pem:
return _get_json_signed(session, url, domain,
session_headers, session_params,
timeout_sec, signing_priv_key_pem,
quiet, debug)
return _get_json_request(session, url, domain, session_headers,
session_params, timeout_sec,
None, quiet, debug, False)
def _set_user_agent(session, http_prefix: str, domain_full: str) -> None:
"""Sets the user agent
"""

View File

@ -11,6 +11,7 @@ import os
import html
import random
import urllib.parse
from utils import get_cached_post_filename
from utils import remove_id_ending
from utils import is_dm
from utils import is_reply
@ -301,9 +302,11 @@ def _speaker_endpoint_json(display_name: str, summary: str,
return speaker_json
def _ssm_lheader(system_language: str, instance_title: str) -> str:
def _ssml_header(system_language: str, box_name: str, summary: str) -> str:
"""Returns a header for an SSML document
"""
if summary:
summary = ': ' + summary
return '<?xml version="1.0"?>\n' + \
'<speak xmlns="http://www.w3.org/2001/10/synthesis"\n' + \
' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n' + \
@ -312,15 +315,14 @@ def _ssm_lheader(system_language: str, instance_title: str) -> str:
' version="1.1">\n' + \
' <metadata>\n' + \
' <dc:title xml:lang="' + system_language + '">' + \
instance_title + ' inbox</dc:title>\n' + \
box_name + summary + '</dc:title>\n' + \
' </metadata>\n'
def _speaker_endpoint_ssml(display_name: str, summary: str,
content: str, image_description: str,
links: [], language: str,
instance_title: str,
gender: str) -> str:
gender: str, box_name: str) -> str:
"""Returns an SSML endpoint for the TTS speaker
https://en.wikipedia.org/wiki/Speech_Synthesis_Markup_Language
https://www.w3.org/TR/speech-synthesis/
@ -342,7 +344,9 @@ def _speaker_endpoint_ssml(display_name: str, summary: str,
content = _add_ssm_lemphasis(content)
voice_params = 'name="' + display_name + '" gender="' + gender + '"'
return _ssm_lheader(lang_short, instance_title) + \
if summary is None:
summary = ''
return _ssml_header(lang_short, box_name, summary) + \
' <p>\n' + \
' <s xml:lang="' + language + '">\n' + \
' <voice ' + voice_params + '>\n' + \
@ -356,7 +360,6 @@ def _speaker_endpoint_ssml(display_name: str, summary: str,
def get_ssml_box(base_dir: str, path: str,
domain: str,
system_language: str,
instance_title: str,
box_name: str) -> str:
"""Returns SSML for the given timeline
"""
@ -379,7 +382,7 @@ def get_ssml_box(base_dir: str, path: str,
speaker_json['imageDescription'],
speaker_json['detectedLinks'],
system_language,
instance_title, gender)
gender, box_name)
def speakable_text(base_dir: str, content: str, translate: {}) -> (str, []):
@ -544,7 +547,8 @@ def update_speaker(base_dir: str, http_prefix: str,
nickname: str, domain: str, domain_full: str,
post_json_object: {}, person_cache: {},
translate: {}, announcing_actor: str,
theme_name: str) -> None:
theme_name: str,
system_language: str, box_name: str) -> None:
""" Generates a json file which can be used for TTS announcement
of incoming inbox posts
"""
@ -554,5 +558,35 @@ def update_speaker(base_dir: str, http_prefix: str,
post_json_object, person_cache,
translate, announcing_actor,
theme_name)
speaker_filename = acct_dir(base_dir, nickname, domain) + '/speaker.json'
if not speaker_json:
return
account_dir = acct_dir(base_dir, nickname, domain)
speaker_filename = account_dir + '/speaker.json'
save_json(speaker_json, speaker_filename)
# save the ssml
cached_ssml_filename = \
get_cached_post_filename(base_dir, nickname,
domain, post_json_object)
if not cached_ssml_filename:
return
cached_ssml_filename = cached_ssml_filename.replace('.html', '.ssml')
if box_name == 'outbox':
cached_ssml_filename = \
cached_ssml_filename.replace('/postcache/', '/outbox/')
gender = None
if speaker_json.get('gender'):
gender = speaker_json['gender']
ssml_str = \
_speaker_endpoint_ssml(speaker_json['name'],
speaker_json['summary'],
speaker_json['say'],
speaker_json['imageDescription'],
speaker_json['detectedLinks'],
system_language,
gender, box_name)
try:
with open(cached_ssml_filename, 'w+') as fp_ssml:
fp_ssml.write(ssml_str)
except OSError:
print('EX: unable to write ssml ' + cached_ssml_filename)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 981 B

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 980 B

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 980 B

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1019 B

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

@ -1745,6 +1745,25 @@ def delete_cached_html(base_dir: str, nickname: str, domain: str,
'unable to delete cached post file ' +
str(cached_post_filename))
cached_post_filename = cached_post_filename.replace('.html', '.ssml')
if os.path.isfile(cached_post_filename):
try:
os.remove(cached_post_filename)
except OSError:
print('EX: delete_cached_html ' +
'unable to delete cached ssml post file ' +
str(cached_post_filename))
cached_post_filename = \
cached_post_filename.replace('/postcache/', '/outbox/')
if os.path.isfile(cached_post_filename):
try:
os.remove(cached_post_filename)
except OSError:
print('EX: delete_cached_html ' +
'unable to delete cached outbox ssml post file ' +
str(cached_post_filename))
def _delete_hashtags_on_post(base_dir: str, post_json_object: {}) -> None:
"""Removes hashtags when a post is deleted

View File

@ -14,6 +14,7 @@ from utils import get_config_param
from utils import acct_dir
from webapp_utils import html_header_with_external_style
from webapp_utils import html_footer
from webapp_utils import get_banner_file
def load_access_keys_for_accounts(base_dir: str, key_shortcuts: {},
@ -43,7 +44,7 @@ def html_access_keys(css_cache: {}, base_dir: str,
nickname: str, domain: str,
translate: {}, access_keys: {},
default_access_keys: {},
default_timeline: str) -> str:
default_timeline: str, theme: str) -> str:
"""Show and edit key shortcuts
"""
access_keys_filename = \
@ -53,6 +54,9 @@ def html_access_keys(css_cache: {}, base_dir: str,
if access_keys_from_file:
access_keys = access_keys_from_file
timeline_key = access_keys['menuTimeline']
submit_key = access_keys['submitButton']
access_keys_form = ''
css_filename = base_dir + '/epicyon-profile.css'
if os.path.isfile(base_dir + '/epicyon.css'):
@ -62,6 +66,20 @@ def html_access_keys(css_cache: {}, base_dir: str,
get_config_param(base_dir, 'instanceTitle')
access_keys_form = \
html_header_with_external_style(css_filename, instance_title, None)
access_keys_form += \
'<header>\n' + \
'<a href="/users/' + nickname + '/' + \
default_timeline + '" title="' + \
translate['Switch to timeline view'] + '" alt="' + \
translate['Switch to timeline view'] + '">\n'
banner_file, _ = \
get_banner_file(base_dir, nickname, domain, theme)
access_keys_form += '<img loading="lazy" decoding="async" ' + \
'class="timeline-banner" alt="" ' + \
'src="/users/' + nickname + '/' + banner_file + '" /></a>\n' + \
'</header>\n'
access_keys_form += '<div class="container">\n'
access_keys_form += \
@ -73,8 +91,6 @@ def html_access_keys(css_cache: {}, base_dir: str,
access_keys_form += ' <form method="POST" action="' + \
'/users/' + nickname + '/changeAccessKeys">\n'
timeline_key = access_keys['menuTimeline']
submit_key = access_keys['submitButton']
access_keys_form += \
' <center>\n' + \
' <button type="submit" class="button" ' + \

View File

@ -57,28 +57,42 @@ def header_buttons_timeline(default_timeline: str,
if default_timeline == 'tlmedia':
tl_str += \
'<a href="' + users_path + '/tlmedia" tabindex="-1" ' + \
'accesskey="' + access_keys['menuMedia'] + '"' + \
'accesskey="' + access_keys['menuMedia'] + '"'
if box_name == 'tlmedia':
tl_str += ' aria-current="location"'
tl_str += \
'><button class="' + \
mediaButton + '"><span>' + translate['Media'] + \
'</span></button></a>'
elif default_timeline == 'tlblogs':
tl_str += \
'<a href="' + users_path + \
'/tlblogs" tabindex="-1"><button class="' + \
'/tlblogs" tabindex="-1"'
if box_name == 'tlblogs':
tl_str += ' aria-current="location"'
tl_str += \
'><button class="' + \
blogs_button + '"><span>' + translate['Blogs'] + \
'</span></button></a>'
elif default_timeline == 'tlfeatures':
tl_str += \
'<a href="' + users_path + \
'/tlfeatures" tabindex="-1"><button class="' + \
'/tlfeatures" tabindex="-1"'
if box_name == 'tlfeatures':
tl_str += ' aria-current="location"'
tl_str += \
'><button class="' + \
features_button + '"><span>' + translate['Features'] + \
'</span></button></a>'
else:
tl_str += \
'<a href="' + users_path + \
'/inbox" tabindex="-1"><button class="' + \
inbox_button + '"><span>' + \
translate['Inbox'] + '</span></button></a>'
inbox_button + '"'
if box_name == 'inbox':
tl_str += ' aria-current="location"'
tl_str += \
'><span>' + translate['Inbox'] + '</span></button></a>'
# if this is a news instance and we are viewing the news timeline
features_header = False
@ -87,8 +101,11 @@ def header_buttons_timeline(default_timeline: str,
if not features_header:
tl_str += \
'<a href="' + users_path + \
'/dm" tabindex="-1"><button class="' + dm_button + \
'<a href="' + users_path + '/dm" tabindex="-1"'
if box_name == 'dm':
tl_str += ' aria-current="location"'
tl_str += \
'><button class="' + dm_button + \
'"><span>' + html_highlight_label(translate['DM'], new_dm) + \
'</span></button></a>'
@ -96,8 +113,11 @@ def header_buttons_timeline(default_timeline: str,
acct_dir(base_dir, nickname, domain) + '/tlreplies.index'
if os.path.isfile(replies_index_filename):
tl_str += \
'<a href="' + users_path + '/tlreplies" tabindex="-1">' + \
'<button class="' + replies_button + '"><span>' + \
'<a href="' + users_path + '/tlreplies" tabindex="-1"'
if box_name == 'tlreplies':
tl_str += ' aria-current="location"'
tl_str += \
'><button class="' + replies_button + '"><span>' + \
html_highlight_label(translate['Replies'], new_reply) + \
'</span></button></a>'
@ -106,15 +126,22 @@ def header_buttons_timeline(default_timeline: str,
if not minimal and not features_header:
tl_str += \
'<a href="' + users_path + '/tlmedia" tabindex="-1" ' + \
'accesskey="' + access_keys['menuMedia'] + '">' + \
'<button class="' + \
'accesskey="' + access_keys['menuMedia'] + '"'
if box_name == 'tlmedia':
tl_str += ' aria-current="location"'
tl_str += \
'><button class="' + \
mediaButton + '"><span>' + translate['Media'] + \
'</span></button></a>'
else:
if not minimal:
tl_str += \
'<a href="' + users_path + \
'/inbox" tabindex="-1"><button class="' + \
'/inbox" tabindex="-1"'
if box_name == 'inbox':
tl_str += ' aria-current="location"'
tl_str += \
'><button class="' + \
inbox_button + '"><span>' + translate['Inbox'] + \
'</span></button></a>'
@ -128,14 +155,22 @@ def header_buttons_timeline(default_timeline: str,
title_str = translate['Article']
tl_str += \
'<a href="' + users_path + \
'/tlblogs" tabindex="-1"><button class="' + \
'/tlblogs" tabindex="-1"'
if box_name == 'tlblogs':
tl_str += ' aria-current="location"'
tl_str += \
'><button class="' + \
blogs_button + '"><span>' + title_str + \
'</span></button></a>'
else:
if not minimal:
tl_str += \
'<a href="' + users_path + \
'/inbox" tabindex="-1"><button class="' + \
'/inbox" tabindex="-1"'
if box_name == 'inbox':
tl_str += ' aria-current="location"'
tl_str += \
'><button class="' + \
inbox_button + '"><span>' + translate['Inbox'] + \
'</span></button></a>'
@ -145,7 +180,11 @@ def header_buttons_timeline(default_timeline: str,
if not features_header:
tl_str += \
'<a href="' + users_path + \
'/inbox" tabindex="-1"><button class="' + \
'/inbox" tabindex="-1"'
if box_name == 'inbox':
tl_str += ' aria-current="location"'
tl_str += \
'><button class="' + \
inbox_button + '"><span>' + translate['Inbox'] + \
'</span></button></a>'
@ -205,7 +244,11 @@ def header_buttons_timeline(default_timeline: str,
if not features_header:
# button for the outbox
tl_str += \
'<a href="' + users_path + '/outbox"><button class="' + \
'<a href="' + users_path + '/outbox"'
if box_name == 'outbox':
tl_str += ' aria-current="location"'
tl_str += \
'><button class="' + \
sent_button + '" tabindex="-1"><span>' + translate['Sent'] + \
'</span></button></a>'
@ -278,8 +321,11 @@ def header_buttons_timeline(default_timeline: str,
if features_header:
tl_str += \
'<a href="' + users_path + '/inbox" tabindex="-1">' + \
'<button class="button">' + \
'<a href="' + users_path + '/inbox" tabindex="-1"'
if box_name == 'inbox':
tl_str += ' aria-current="location"'
tl_str += \
'><button class="button">' + \
'<span>' + translate['User'] + '</span></button></a>'
# the newswire button to show right column links

View File

@ -1698,7 +1698,8 @@ def individual_post_as_html(signing_priv_key_pem: str,
nickname, domain, domain_full,
post_json_object, person_cache,
translate, post_json_object['actor'],
theme_name)
theme_name, system_language,
box_name)
with open(announce_filename + '.tts', 'w+') as ttsfile:
ttsfile.write('\n')

View File

@ -419,13 +419,21 @@ def _page_number_buttons(users_path: str, box_name: str,
for page in range(min_page_number, max_page_number):
if num_str:
num_str += ''
aria_page_str = ''
page_str = str(page)
if page == page_number:
page_str = '<mark>' + str(page) + '</mark>'
aria_page_str = ' aria-current="true"'
num_str += \
'<a href="' + users_path + '/' + box_name + '?page=' + \
str(page) + '" class="pageslist">' + page_str + '</a>'
return '<center>' + num_str + '</center>'
str(page) + '" class="pageslist" ' + \
'aria-label="Current Page, Page ' + str(page) + \
'"' + aria_page_str + '>' + page_str + '</a>'
return '<center>\n' + \
' <nav role="navigation" aria-label="Pagination Navigation">\n' + \
' ' + num_str + '\n' + \
' </nav>\n' + \
'</center>\n'
def html_timeline(css_cache: {}, default_timeline: str,
@ -656,8 +664,11 @@ def html_timeline(css_cache: {}, default_timeline: str,
moderation_button_str = ''
if moderator and not minimal:
moderation_button_str = \
'<a href="' + users_path + \
'/moderation"><button class="' + \
'<a href="' + users_path + '/moderation"'
if box_name == 'moderation':
moderation_button_str += ' aria-current="location"'
moderation_button_str += \
'><button class="' + \
moderation_button + '"><span>' + \
html_highlight_label(translate['Mod'], new_report) + \
' </span></button></a>'
@ -669,19 +680,30 @@ def html_timeline(css_cache: {}, default_timeline: str,
events_button_str = ''
if not minimal:
shares_button_str = \
'<a href="' + users_path + '/tlshares"><button class="' + \
shares_button + '"><span>' + \
'<a href="' + users_path + '/tlshares"'
if box_name == 'tlshares':
shares_button_str += ' aria-current="location"'
shares_button_str += \
'><button class="' + shares_button + '"><span>' + \
html_highlight_label(translate['Shares'], new_share) + \
'</span></button></a>'
wanted_button_str = \
'<a href="' + users_path + '/tlwanted"><button class="' + \
wanted_button + '"><span>' + \
wanted_button + '"'
if box_name == 'tlwanted':
wanted_button_str += ' aria-current="location"'
wanted_button_str += \
'><span>' + \
html_highlight_label(translate['Wanted'], new_wanted) + \
'</span></button></a>'
bookmarks_button_str = \
'<a href="' + users_path + '/tlbookmarks"><button class="' + \
'<a href="' + users_path + '/tlbookmarks"'
if box_name == 'tlbookmarks':
bookmarks_button_str += ' aria-current="location"'
bookmarks_button_str += \
'><button class="' + \
bookmarks_button + '"><span>' + translate['Bookmarks'] + \
'</span></button></a>'