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

merge-requests/30/head
Bob Mottram 2022-06-28 23:27:03 +01:00
commit aead5d2cf9
57 changed files with 2037 additions and 294 deletions

View File

@ -313,7 +313,7 @@ def _html_blog_post_content(debug: bool, session, authorized: bool,
if citations_str:
citations_str = '<p><b>' + translate['Citations'] + \
':</b></p>' + \
'<ul>\n' + citations_str + '</ul>\n'
'<u>\n' + citations_str + '</u>\n'
blog_str += '<br>\n' + citations_str

110
daemon.py
View File

@ -174,6 +174,7 @@ from webapp_utils import get_pwa_theme_colors
from webapp_calendar import html_calendar_delete_confirm
from webapp_calendar import html_calendar
from webapp_about import html_about
from webapp_specification import html_specification
from webapp_accesskeys import html_access_keys
from webapp_accesskeys import load_access_keys_for_accounts
from webapp_confirm import html_confirm_delete
@ -4782,6 +4783,7 @@ class PubServer(BaseHTTPRequestHandler):
links_filename = base_dir + '/accounts/links.txt'
about_filename = base_dir + '/accounts/about.md'
tos_filename = base_dir + '/accounts/tos.md'
specification_filename = base_dir + '/accounts/activitypub.md'
# extract all of the text fields into a dict
fields = \
@ -4860,6 +4862,23 @@ class PubServer(BaseHTTPRequestHandler):
print('EX: _links_update unable to delete ' +
tos_filename)
if fields.get('editedSpecification'):
specification_str = fields['editedSpecification']
try:
with open(specification_filename, 'w+',
encoding='utf-8') as specificationfile:
specificationfile.write(specification_str)
except OSError:
print('EX: unable to write specification ' +
specification_filename)
else:
if os.path.isfile(specification_filename):
try:
os.remove(specification_filename)
except OSError:
print('EX: _links_update unable to delete ' +
specification_filename)
# redirect back to the default timeline
self._redirect_headers(actor_str + '/' + default_timeline,
cookie, calling_domain)
@ -8212,6 +8231,56 @@ class PubServer(BaseHTTPRequestHandler):
return
self._404()
def _show_specification_image(self, path: str,
base_dir: str, getreq_start_time) -> None:
"""Shows an image within the ActivityPub specification document
"""
image_filename = path.split('/', 1)[1]
if '/' in image_filename:
self._404()
return
media_filename = \
base_dir + '/specification/' + image_filename
if self._etag_exists(media_filename):
# The file has not changed
self._304()
return
if self.server.iconsCache.get(media_filename):
media_binary = self.server.iconsCache[media_filename]
mime_type_str = media_file_mime_type(media_filename)
self._set_headers_etag(media_filename,
mime_type_str,
media_binary, None,
self.server.domain_full,
False, None)
self._write(media_binary)
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', '_show_specification_image',
self.server.debug)
return
if os.path.isfile(media_filename):
media_binary = None
try:
with open(media_filename, 'rb') as av_file:
media_binary = av_file.read()
except OSError:
print('EX: unable to read specification image ' +
media_filename)
if media_binary:
mime_type = media_file_mime_type(media_filename)
self._set_headers_etag(media_filename,
mime_type,
media_binary, None,
self.server.domain_full,
False, None)
self._write(media_binary)
self.server.iconsCache[media_filename] = media_binary
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', '_show_specification_image',
self.server.debug)
return
self._404()
def _show_help_screen_image(self, calling_domain: str, path: str,
base_dir: str, getreq_start_time) -> None:
"""Shows a help screen image
@ -16274,6 +16343,39 @@ class PubServer(BaseHTTPRequestHandler):
self.server.debug)
return
if self.path in ('/specification', '/protocol', '/activitypub'):
if calling_domain.endswith('.onion'):
msg = \
html_specification(self.server.css_cache,
self.server.base_dir, 'http',
self.server.onion_domain,
None, self.server.translate,
self.server.system_language)
elif calling_domain.endswith('.i2p'):
msg = \
html_specification(self.server.css_cache,
self.server.base_dir, 'http',
self.server.i2p_domain,
None, self.server.translate,
self.server.system_language)
else:
msg = \
html_specification(self.server.css_cache,
self.server.base_dir,
self.server.http_prefix,
self.server.domain_full,
self.server.onion_domain,
self.server.translate,
self.server.system_language)
msg = msg.encode('utf-8')
msglen = len(msg)
self._login_headers('text/html', msglen, calling_domain)
self._write(msg)
fitness_performance(getreq_start_time, self.server.fitness,
'_GET', 'show specification screen',
self.server.debug)
return
if html_getreq and users_in_path and authorized and \
self.path.endswith('/accesskeys'):
nickname = self.path.split('/users/')[1]
@ -16683,6 +16785,14 @@ class PubServer(BaseHTTPRequestHandler):
self.server.base_dir, getreq_start_time)
return
# show images within https://instancedomain/activitypub
if self.path.startswith('/activitypub-tutorial-'):
if self.path.endswith('.png'):
self._show_specification_image(self.path,
self.server.base_dir,
getreq_start_time)
return
# help screen images
# Note that this comes before the busy flag to avoid conflicts
if self.path.startswith('/helpimages/'):

File diff suppressed because one or more lines are too long

View File

@ -21,6 +21,7 @@
--header-bg-color: #282c37;
--main-bg-color: #282c37;
--post-bg-color: #282c37;
--code-color: lightblue;
--column-left-color: #282c37;
--link-bg-color: #282c37;
--dropdown-fg-color: #dddddd;
@ -127,6 +128,7 @@
--quote-font-size-mobile: 120%;
--quote-font-size-tiny: 60%;
--line-spacing: 180%;
--code-spacing: 100%;
--line-spacing-newswire: 120%;
--newswire-item-moderated-color: white;
--newswire-date-moderated-color: white;
@ -248,6 +250,16 @@ body, html {
image-rendering: var(--rendering);
}
code {
color: var(--code-color);
white-space: pre;
line-height: var(--code-spacing);
}
ul.md_list {
color: var(--main-fg-color);
}
audio {
width: 100%;
}

View File

@ -8,6 +8,38 @@ __status__ = "Production"
__module_group__ = "Web Interface"
def _markdown_get_sections(markdown: str) -> []:
"""Returns a list of sections for markdown
"""
if '<code>' not in markdown:
return [markdown]
lines = markdown.split('\n')
sections = []
section_text = ''
section_active = False
ctr = 0
for line in lines:
if ctr > 0:
section_text += '\n'
if not section_active:
if '<code>' in line:
section_active = True
sections.append(section_text)
section_text = ''
else:
if '</code>' in line:
section_active = False
sections.append(section_text)
section_text = ''
section_text += line
ctr += 1
if section_text.strip():
sections.append(section_text)
return sections
def _markdown_emphasis_html(markdown: str) -> str:
"""Add italics and bold html markup to the given markdown
"""
@ -17,39 +49,73 @@ def _markdown_emphasis_html(markdown: str) -> str:
'**.': '</b>.',
'**:': '</b>:',
'**;': '</b>;',
'?**': '?</b>',
'\n**': '\n<b>',
'**,': '</b>,',
'**\n': '</b>\n',
'(**': '(<b>)',
'**)': '</b>)',
'>**': '><b>',
'**<': '</b><',
'>*': '><i>',
'*<': '</i><',
' *': ' <i>',
'* ': '</i> ',
'?*': '?</i>',
'\n*': '\n<i>',
'*.': '</i>.',
'*:': '</i>:',
'*;': '</i>;',
'(*': '(<i>)',
'*)': '</i>)',
'*,': '</i>,',
'*\n': '</i>\n',
' _': ' <ul>',
'_ ': '</ul> ',
'_.': '</ul>.',
'_:': '</ul>:',
'_;': '</ul>;',
'_,': '</ul>,',
'_\n': '</ul>\n'
'(_': '(<u>',
'_)': '</u>)',
' _': ' <u>',
'_ ': '</u> ',
'_.': '</u>.',
'_:': '</u>:',
'_;': '</u>;',
'_,': '</u>,',
'_\n': '</u>\n',
' `': ' <em>',
'`.': '</em>.',
'`:': '</em>:',
"`'": "</em>'",
"(`": "(<em>",
"`)": "</em>)",
'`;': '</em>;',
'`,': '</em>,',
'`\n': '</em>\n',
'` ': '</em> '
}
for md_str, html in replacements.items():
markdown = markdown.replace(md_str, html)
if markdown.startswith('**'):
markdown = markdown[2:] + '<b>'
elif markdown.startswith('*'):
markdown = markdown[1:] + '<i>'
elif markdown.startswith('_'):
markdown = markdown[1:] + '<ul>'
sections = _markdown_get_sections(markdown)
markdown = ''
for section_text in sections:
if '<code>' in section_text:
markdown += section_text
continue
for md_str, html in replacements.items():
section_text = section_text.replace(md_str, html)
if markdown.endswith('**'):
markdown = markdown[:len(markdown) - 2] + '</b>'
elif markdown.endswith('*'):
markdown = markdown[:len(markdown) - 1] + '</i>'
elif markdown.endswith('_'):
markdown = markdown[:len(markdown) - 1] + '</ul>'
if section_text.startswith('**'):
section_text = section_text[2:] + '<b>'
elif section_text.startswith('*'):
section_text = section_text[1:] + '<i>'
elif section_text.startswith('_'):
section_text = section_text[1:] + '<u>'
if section_text.endswith('**'):
section_text = section_text[:len(section_text) - 2] + '</b>'
elif section_text.endswith('*'):
section_text = section_text[:len(section_text) - 1] + '</i>'
elif section_text.endswith('_'):
section_text = section_text[:len(section_text) - 1] + '</u>'
if section_text.strip():
markdown += section_text
return markdown
@ -61,7 +127,19 @@ def _markdown_replace_quotes(markdown: str) -> str:
lines = markdown.split('\n')
result = ''
prev_quote_line = None
code_section = False
for line in lines:
# avoid code sections
if not code_section:
if '<code>' in line:
code_section = True
else:
if '</code>' in line:
code_section = False
if code_section:
result += line + '\n'
continue
if '> ' not in line:
result += line + '\n'
prev_quote_line = None
@ -90,56 +168,176 @@ def _markdown_replace_quotes(markdown: str) -> str:
return result
def _markdown_replace_links(markdown: str, images: bool = False) -> str:
def _markdown_replace_links(markdown: str) -> str:
"""Replaces markdown links with html
Optionally replace image links
"""
replace_links = {}
text = markdown
start_chars = '['
if images:
start_chars = '!['
while start_chars in text:
if ')' not in text:
break
text = text.split(start_chars, 1)[1]
markdown_link = start_chars + text.split(')')[0] + ')'
if ']' not in markdown_link or \
'(' not in markdown_link:
text = text.split(')', 1)[1]
sections = _markdown_get_sections(markdown)
result = ''
for section_text in sections:
if '<code>' in section_text or \
'](' not in section_text:
result += section_text
continue
if not images:
replace_links[markdown_link] = \
'<a href="' + \
markdown_link.split('(')[1].split(')')[0] + \
'" target="_blank" rel="nofollow noopener noreferrer">' + \
markdown_link.split(start_chars)[1].split(']')[0] + \
'</a>'
sections_links = section_text.split('](')
ctr = 0
for link_section in sections_links:
if ctr == 0:
ctr += 1
continue
if '[' in sections_links[ctr - 1] and \
')' in link_section:
link_text = sections_links[ctr - 1].split('[')[-1]
link_url = link_section.split(')')[0]
replace_str = '[' + link_text + '](' + link_url + ')'
link_text = link_text.replace('`', '')
if '!' + replace_str in section_text:
html_link = \
'<img class="markdownImage" src="' + \
link_url + '" alt="' + link_text + '" />'
section_text = \
section_text.replace('!' + replace_str, html_link)
if replace_str in section_text:
html_link = \
'<a href="' + link_url + '" target="_blank" ' + \
'rel="nofollow noopener noreferrer">' + \
link_text + '</a>'
section_text = \
section_text.replace(replace_str, html_link)
ctr += 1
result += section_text
return result
def _markdown_replace_bullet_points(markdown: str) -> str:
"""Replaces bullet points
"""
lines = markdown.split('\n')
bullet_style = ('* ', ' * ', '- ', ' - ')
bullet_matched = ''
start_line = -1
line_ctr = 0
changed = False
code_section = False
for line in lines:
if not line.strip():
# skip blank lines
line_ctr += 1
continue
# skip over code sections
if not code_section:
if '<code>' in line:
code_section = True
else:
replace_links[markdown_link] = \
'<img class="markdownImage" src="' + \
markdown_link.split('(')[1].split(')')[0] + \
'" alt="' + \
markdown_link.split(start_chars)[1].split(']')[0] + \
'" />'
text = text.split(')', 1)[1]
for md_link, html_link in replace_links.items():
markdown = markdown.replace(md_link, html_link)
if '</code>' in line:
code_section = False
if code_section:
line_ctr += 1
continue
if not bullet_matched:
for test_str in bullet_style:
if line.startswith(test_str):
bullet_matched = test_str
start_line = line_ctr
break
else:
if not line.startswith(bullet_matched):
for index in range(start_line, line_ctr):
line_text = lines[index].replace(bullet_matched, '', 1)
if index == start_line:
lines[index] = \
'<ul class="md_list">\n<li>' + line_text + '</li>'
elif index == line_ctr - 1:
lines[index] = '<li>' + line_text + '</li>\n</ul>'
else:
lines[index] = '<li>' + line_text + '</li>'
changed = True
start_line = -1
bullet_matched = ''
line_ctr += 1
if not changed:
return markdown
markdown = ''
for line in lines:
markdown += line + '\n'
return markdown
def _markdown_replace_code(markdown: str) -> str:
"""Replaces code sections within markdown
"""
lines = markdown.split('\n')
start_line = -1
line_ctr = 0
changed = False
section_active = False
for line in lines:
if not line.strip():
# skip blank lines
line_ctr += 1
continue
if line.startswith('```'):
if not section_active:
start_line = line_ctr
section_active = True
else:
lines[start_line] = '<code>'
lines[line_ctr] = '</code>'
section_active = False
changed = True
line_ctr += 1
if not changed:
return markdown
markdown = ''
for line in lines:
markdown += line + '\n'
return markdown
def markdown_example_numbers(markdown: str) -> str:
"""Ensures that example numbers in the ActivityPub specification
document are sequential
"""
lines = markdown.split('\n')
example_number = 1
line_ctr = 0
for line in lines:
if not line.strip():
# skip blank lines
line_ctr += 1
continue
if line.startswith('##') and '## Example ' in line:
header_str = line.split(' Example ')[0]
lines[line_ctr] = header_str + ' Example ' + str(example_number)
example_number += 1
line_ctr += 1
markdown = ''
for line in lines:
markdown += line + '\n'
return markdown
def markdown_to_html(markdown: str) -> str:
"""Converts markdown formatted text to html
"""
markdown = _markdown_replace_code(markdown)
markdown = _markdown_replace_bullet_points(markdown)
markdown = _markdown_replace_quotes(markdown)
markdown = _markdown_emphasis_html(markdown)
markdown = _markdown_replace_links(markdown, True)
markdown = _markdown_replace_links(markdown)
# replace headers
lines_list = markdown.split('\n')
html_str = ''
ctr = 0
code_section = False
titles = {
"h5": '#####',
"h4": '####',
@ -149,13 +347,37 @@ def markdown_to_html(markdown: str) -> str:
}
for line in lines_list:
if ctr > 0:
html_str += '<br>'
if not code_section:
html_str += '<br>\n'
else:
html_str += '\n'
# avoid code sections
if not code_section:
if '<code>' in line:
code_section = True
else:
if '</code>' in line:
code_section = False
if code_section:
html_str += line
ctr += 1
continue
for hsh, hashes in titles.items():
if line.startswith(hashes):
line = line.replace(hashes, '').strip()
line = '<' + hsh + '>' + line + '</' + hsh + '>'
line = '<' + hsh + '>' + line + '</' + hsh + '>\n'
ctr = -1
break
html_str += line
ctr += 1
html_str = html_str.replace('<code><br>', '<code>')
html_str = html_str.replace('</code><br>', '</code>')
html_str = html_str.replace('<ul class="md_list"><br>',
'<ul class="md_list">')
html_str = html_str.replace('</li><br>', '</li>')
return html_str

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

File diff suppressed because it is too large Load Diff

View File

@ -5715,16 +5715,68 @@ def _test_markdown_to_html():
markdown = 'This is a quotation:\n' + \
'> Some quote or other'
assert markdown_to_html(markdown) == 'This is a quotation:<br>' + \
expected = \
'This is a quotation:<br>\n' + \
'<blockquote><i>Some quote or other</i></blockquote>'
result = markdown_to_html(markdown)
if result != expected:
print(result)
assert result == expected
markdown = 'This is a multi-line quotation:\n' + \
'> The first line\n' + \
'> The second line'
assert markdown_to_html(markdown) == \
'This is a multi-line quotation:<br>' + \
'This is a multi-line quotation:<br>\n' + \
'<blockquote><i>The first line The second line</i></blockquote>'
markdown = 'This is a list of points:\n' + \
' * Point 1\n' + \
' * Point 2\n\n' + \
'And some other text.'
result = markdown_to_html(markdown)
expected = \
'This is a list of points:<br>\n<ul class="md_list">' + \
'\n<li>Point 1</li>\n' + \
'<li>Point 2</li>\n<li></li>\n</ul><br>\n' + \
'And some other text.<br>\n'
if result != expected:
print(result)
assert result == expected
markdown = 'This is a list of points:\n' + \
' * **Point 1**\n' + \
' * *Point 2*\n\n' + \
'And some other text.'
result = markdown_to_html(markdown)
expected = \
'This is a list of points:<br>\n<ul class="md_list">\n' + \
'<li><b>Point 1</b></li>\n' + \
'<li><i>Point 2</i></li>\n<li></li>\n</ul><br>\n' + \
'And some other text.<br>\n'
if result != expected:
print(result)
assert result == expected
markdown = 'This is a code section:\n' + \
'``` json\n' + \
'10 PRINT "YOLO"\n' + \
'20 GOTO 10\n' + \
'```\n\n' + \
'And some other text.'
result = markdown_to_html(markdown)
expected = \
'This is a code section:<br>\n' + \
'<code>\n' + \
'10 PRINT "YOLO"\n' + \
'20 GOTO 10\n' + \
'</code>\n' + \
'<br>\n' + \
'And some other text.<br>\n'
if result != expected:
print(result)
assert result == expected
markdown = 'This is **bold**'
assert markdown_to_html(markdown) == 'This is <b>bold</b>'
@ -5732,27 +5784,34 @@ def _test_markdown_to_html():
assert markdown_to_html(markdown) == 'This is <i>italic</i>'
markdown = 'This is _underlined_'
assert markdown_to_html(markdown) == 'This is <ul>underlined</ul>'
assert markdown_to_html(markdown) == 'This is <u>underlined</u>'
markdown = 'This is **just** plain text'
assert markdown_to_html(markdown) == 'This is <b>just</b> plain text'
markdown = '# Title1\n### Title3\n## Title2\n'
assert markdown_to_html(markdown) == \
'<h1>Title1</h1><h3>Title3</h3><h2>Title2</h2>'
expected = '<h1>Title1</h1>\n<h3>Title3</h3>\n<h2>Title2</h2>\n'
result = markdown_to_html(markdown)
if result != expected:
print(result)
assert result == expected
markdown = \
'This is [a link](https://something.somewhere) to something.\n' + \
'And [something else](https://cat.pic).\n' + \
'Or ![pounce](/cat.jpg).'
assert markdown_to_html(markdown) == \
expected = \
'This is <a href="https://something.somewhere" ' + \
'target="_blank" rel="nofollow noopener noreferrer">' + \
'a link</a> to something.<br>' + \
'a link</a> to something.<br>\n' + \
'And <a href="https://cat.pic" ' + \
'target="_blank" rel="nofollow noopener noreferrer">' + \
'something else</a>.<br>' + \
'something else</a>.<br>\n' + \
'Or <img class="markdownImage" src="/cat.jpg" alt="pounce" />.'
result = markdown_to_html(markdown)
if result != expected:
print(result)
assert result == expected
def _test_extract_text_fields_from_post():

View File

@ -1,4 +1,5 @@
{
"code-color": "white",
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
"pwa-theme-background-color": "black-translucent",
"newswire-publish-icon": "True",

View File

@ -1,4 +1,5 @@
{
"code-color": "blue",
"diff-add": "#111",
"diff-remove": "#333",
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",

View File

@ -12,6 +12,7 @@
"liker-names-margin": "2%",
"liker-names-vertical-spacing1": "50px",
"liker-names-vertical-spacing2": "100px",
"code-color": "lightblue",
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
"pwa-theme-background-color": "black-translucent",
"avatar-rounding": "10%",

View File

@ -1,4 +1,5 @@
{
"code-color": "lightblue",
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
"pwa-theme-background-color": "black-translucent",
"dropdown-fg-color": "#9ad791",

View File

@ -1,4 +1,5 @@
{
"code-color": "blue",
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
"pwa-theme-background-color": "black-translucent",
"dropdown-fg-color": "white",

View File

@ -1,4 +1,5 @@
{
"code-color": "lightblue",
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
"pwa-theme-background-color": "black-translucent",
"dropdown-fg-color": "white",

View File

@ -1,4 +1,5 @@
{
"code-color": "blue",
"diff-add": "#111",
"diff-remove": "#333",
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",

View File

@ -1,4 +1,5 @@
{
"code-color": "blue",
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
"pwa-theme-background-color": "black-translucent",
"dropdown-fg-color": "#33390d",

View File

@ -1,6 +1,7 @@
{
"diff-add": "#111",
"diff-remove": "#333",
"code-color": "blue",
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
"pwa-theme-background-color": "black-translucent",
"dropdown-fg-color": "#2d2c37",

View File

@ -1,4 +1,5 @@
{
"code-color": "lightblue",
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
"pwa-theme-background-color": "black-translucent",
"dropdown-fg-color": "#0481f5",

View File

@ -1,4 +1,5 @@
{
"code-color": "blue",
"diff-add": "#111",
"diff-remove": "#333",
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",

View File

@ -1,4 +1,5 @@
{
"code-color": "lightblue",
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
"pwa-theme-background-color": "black-translucent",
"dropdown-fg-color": "#f98bb0",

View File

@ -1,4 +1,5 @@
{
"code-color": "lightblue",
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
"pwa-theme-background-color": "black-translucent",
"dropdown-fg-color": "white",

View File

@ -1,4 +1,5 @@
{
"code-color": "blue",
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
"pwa-theme-background-color": "black-translucent",
"dropdown-fg-color": "#2d2c37",

View File

@ -1,4 +1,5 @@
{
"code-color": "lightblue",
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
"pwa-theme-background-color": "black-translucent",
"dropdown-fg-color": "#ffc4bc",

View File

@ -1,4 +1,5 @@
{
"code-color": "blue",
"font-size-likes-mobile": "64px",
"likes-margin-left-mobile": "20px",
"likes-margin-right-mobile": "0px",

View File

@ -564,5 +564,6 @@
"Save": "يحفظ",
"Switch to moderation view": "قم بالتبديل إلى عرض الاعتدال",
"Minimize attached images": "تصغير الصور المرفقة",
"SHOW MEDIA": "عرض الوسائط"
"SHOW MEDIA": "عرض الوسائط",
"ActivityPub Specification": "مواصفات ActivityPub"
}

View File

@ -564,5 +564,6 @@
"Save": "সংরক্ষণ",
"Switch to moderation view": "সংযম দৃশ্যে স্যুইচ করুন",
"Minimize attached images": "সংযুক্ত ছবি ছোট করুন",
"SHOW MEDIA": "মিডিয়া দেখান"
"SHOW MEDIA": "মিডিয়া দেখান",
"ActivityPub Specification": "ActivityPub স্পেসিফিকেশন"
}

View File

@ -564,5 +564,6 @@
"Save": "Desa",
"Switch to moderation view": "Canvia a la visualització de moderació",
"Minimize attached images": "Minimitzar les imatges adjuntes",
"SHOW MEDIA": "MOSTRA ELS MITJANS"
"SHOW MEDIA": "MOSTRA ELS MITJANS",
"ActivityPub Specification": "Especificació d'ActivityPub"
}

View File

@ -564,5 +564,6 @@
"Save": "Arbed",
"Switch to moderation view": "Newid i wedd safoni",
"Minimize attached images": "Lleihau delweddau sydd ynghlwm",
"SHOW MEDIA": "DANGOS CYFRYNGAU"
"SHOW MEDIA": "DANGOS CYFRYNGAU",
"ActivityPub Specification": "Manyleb GweithgareddPub"
}

View File

@ -564,5 +564,6 @@
"Save": "Speichern",
"Switch to moderation view": "Wechseln Sie zur Moderationsansicht",
"Minimize attached images": "Angehängte Bilder minimieren",
"SHOW MEDIA": "MEDIEN ZEIGEN"
"SHOW MEDIA": "MEDIEN ZEIGEN",
"ActivityPub Specification": "ActivityPub-Spezifikation"
}

View File

@ -564,5 +564,6 @@
"Save": "Αποθηκεύσετε",
"Switch to moderation view": "Μετάβαση σε προβολή εποπτείας",
"Minimize attached images": "Ελαχιστοποιήστε τις συνημμένες εικόνες",
"SHOW MEDIA": "ΔΕΙΤΕ ΜΕΣΑ"
"SHOW MEDIA": "ΔΕΙΤΕ ΜΕΣΑ",
"ActivityPub Specification": "Προδιαγραφές ActivityPub"
}

View File

@ -564,5 +564,6 @@
"Save": "Save",
"Switch to moderation view": "Switch to moderation view",
"Minimize attached images": "Minimize attached images",
"SHOW MEDIA": "SHOW MEDIA"
"SHOW MEDIA": "SHOW MEDIA",
"ActivityPub Specification": "ActivityPub Specification"
}

View File

@ -564,5 +564,6 @@
"Save": "Ahorrar",
"Switch to moderation view": "Cambiar a la vista de moderación",
"Minimize attached images": "Minimizar imágenes adjuntas",
"SHOW MEDIA": "MOSTRAR MEDIOS"
"SHOW MEDIA": "MOSTRAR MEDIOS",
"ActivityPub Specification": "Especificación de ActivityPub"
}

View File

@ -564,5 +564,6 @@
"Save": "Sauvegarder",
"Switch to moderation view": "Passer en mode modération",
"Minimize attached images": "Réduire les images jointes",
"SHOW MEDIA": "AFFICHER LES MÉDIAS"
"SHOW MEDIA": "AFFICHER LES MÉDIAS",
"ActivityPub Specification": "Spécification ActivityPub"
}

View File

@ -564,5 +564,6 @@
"Save": "Sábháil",
"Switch to moderation view": "Athraigh go dtí an t-amharc modhnóireachta",
"Minimize attached images": "Íoslaghdaigh íomhánna ceangailte",
"SHOW MEDIA": "Taispeáin MEÁIN"
"SHOW MEDIA": "Taispeáin MEÁIN",
"ActivityPub Specification": "Sonraíocht ActivityPub"
}

View File

@ -564,5 +564,6 @@
"Save": "बचाना",
"Switch to moderation view": "मॉडरेशन दृश्य पर स्विच करें",
"Minimize attached images": "संलग्न छवियों को छोटा करें",
"SHOW MEDIA": "मीडिया दिखाएं"
"SHOW MEDIA": "मीडिया दिखाएं",
"ActivityPub Specification": "गतिविधिपब विशिष्टता"
}

View File

@ -564,5 +564,6 @@
"Save": "Salva",
"Switch to moderation view": "Passa alla visualizzazione moderazione",
"Minimize attached images": "Riduci al minimo le immagini allegate",
"SHOW MEDIA": "MOSTRA MEDIA"
"SHOW MEDIA": "MOSTRA MEDIA",
"ActivityPub Specification": "Specifica ActivityPub"
}

View File

@ -564,5 +564,6 @@
"Save": "保存",
"Switch to moderation view": "モデレートビューに切り替えます",
"Minimize attached images": "添付画像を最小限に抑える",
"SHOW MEDIA": "メディアを表示"
"SHOW MEDIA": "メディアを表示",
"ActivityPub Specification": "ActivityPubの仕様"
}

View File

@ -564,5 +564,6 @@
"Save": "구하다",
"Switch to moderation view": "검토 보기로 전환",
"Minimize attached images": "첨부된 이미지 최소화",
"SHOW MEDIA": "미디어 표시"
"SHOW MEDIA": "미디어 표시",
"ActivityPub Specification": "ActivityPub 사양"
}

View File

@ -564,5 +564,6 @@
"Save": "Rizgarkirin",
"Switch to moderation view": "Biguherîne bo dîtina moderatoriyê",
"Minimize attached images": "Wêneyên pêvekirî kêm bikin",
"SHOW MEDIA": "MEDYA NÎŞAN DE"
"SHOW MEDIA": "MEDYA NÎŞAN DE",
"ActivityPub Specification": "Specification ActivityPub"
}

View File

@ -564,5 +564,6 @@
"Save": "Opslaan",
"Switch to moderation view": "Overschakelen naar moderatieweergave",
"Minimize attached images": "Bijgevoegde afbeeldingen minimaliseren",
"SHOW MEDIA": "TOON MEDIA"
"SHOW MEDIA": "TOON MEDIA",
"ActivityPub Specification": "ActivityPub-specificatie"
}

View File

@ -560,5 +560,6 @@
"Save": "Save",
"Switch to moderation view": "Switch to moderation view",
"Minimize attached images": "Minimize attached images",
"SHOW MEDIA": "SHOW MEDIA"
"SHOW MEDIA": "SHOW MEDIA",
"ActivityPub Specification": "ActivityPub Specification"
}

View File

@ -564,5 +564,6 @@
"Save": "Ratować",
"Switch to moderation view": "Przełącz na widok moderacji",
"Minimize attached images": "Zminimalizuj załączone obrazy",
"SHOW MEDIA": "POKAŻ MEDIA"
"SHOW MEDIA": "POKAŻ MEDIA",
"ActivityPub Specification": "Specyfikacja ActivityPub"
}

View File

@ -564,5 +564,6 @@
"Save": "Salvar",
"Switch to moderation view": "Mudar para a visualização de moderação",
"Minimize attached images": "Minimizar imagens anexadas",
"SHOW MEDIA": "MOSTRAR MÍDIA"
"SHOW MEDIA": "MOSTRAR MÍDIA",
"ActivityPub Specification": "Especificação do ActivityPub"
}

View File

@ -564,5 +564,6 @@
"Save": "Сохранять",
"Switch to moderation view": "Перейти в режим модерации",
"Minimize attached images": "Свернуть прикрепленные изображения",
"SHOW MEDIA": "ПОКАЗАТЬ МЕДИА"
"SHOW MEDIA": "ПОКАЗАТЬ МЕДИА",
"ActivityPub Specification": "Спецификация ActivityPub"
}

View File

@ -564,5 +564,6 @@
"Save": "Hifadhi",
"Switch to moderation view": "Badili hadi mwonekano wa udhibiti",
"Minimize attached images": "Punguza picha zilizoambatishwa",
"SHOW MEDIA": "ONESHA VYOMBO VYA HABARI"
"SHOW MEDIA": "ONESHA VYOMBO VYA HABARI",
"ActivityPub Specification": "Vipimo vya ActivityPub"
}

View File

@ -564,5 +564,6 @@
"Save": "Kaydetmek",
"Switch to moderation view": "Denetleme görünümüne geç",
"Minimize attached images": "Ekli resimleri simge durumuna küçült",
"SHOW MEDIA": "MEDYA GÖSTER"
"SHOW MEDIA": "MEDYA GÖSTER",
"ActivityPub Specification": "ActivityPub Spesifikasyonu"
}

View File

@ -564,5 +564,6 @@
"Save": "Зберегти",
"Switch to moderation view": "Перейти до режиму модерації",
"Minimize attached images": "Мінімізуйте вкладені зображення",
"SHOW MEDIA": "ПОКАЗАТИ ЗМІ"
"SHOW MEDIA": "ПОКАЗАТИ ЗМІ",
"ActivityPub Specification": "Специфікація ActivityPub"
}

View File

@ -564,5 +564,6 @@
"Save": "היט",
"Switch to moderation view": "באַשטימען צו מאַדעריישאַן מיינונג",
"Minimize attached images": "מינאַמייז אַטאַטשט בילדער",
"SHOW MEDIA": "ווייַז מעדיע"
"SHOW MEDIA": "ווייַז מעדיע",
"ActivityPub Specification": "ActivityPub באַשרייַבונג"
}

View File

@ -564,5 +564,6 @@
"Save": "节省",
"Switch to moderation view": "切换到审核视图",
"Minimize attached images": "最小化附加图像",
"SHOW MEDIA": "展示媒体"
"SHOW MEDIA": "展示媒体",
"ActivityPub Specification": "ActivityPub 规范"
}

View File

@ -346,6 +346,9 @@ def get_left_column_content(base_dir: str, nickname: str, domain_full: str,
html_str += \
'<p class="login-text"><a href="/about">' + \
translate['About this Instance'] + '</a></p>'
html_str += \
'<p class="login-text"><a href="/activitypub">' + \
translate['ActivityPub Specification'] + '</a></p>'
html_str += \
'<p class="login-text"><a href="/terms">' + \
translate['Terms of Service'] + '</a></p>'
@ -505,7 +508,7 @@ def html_edit_links(css_cache: {}, translate: {}, base_dir: str, path: str,
edit_links_form += \
'</div>'
# the admin can edit terms of service and about text
# the admin can edit terms of service, about and specification text
admin_nickname = get_config_param(base_dir, 'admin')
if admin_nickname:
if nickname == admin_nickname:
@ -547,5 +550,25 @@ def html_edit_links(css_cache: {}, translate: {}, base_dir: str, path: str,
edit_links_form += \
'</div>'
specification_filename = base_dir + '/accounts/activitypub.md'
specification_str = ''
if os.path.isfile(specification_filename):
with open(specification_filename, 'r',
encoding='utf-8') as fp_specification:
specification_str = fp_specification.read()
edit_links_form += \
'<div class="container">'
edit_links_form += \
' ' + \
translate['ActivityPub Specification'] + \
'<br>'
edit_links_form += \
' <textarea id="message" name="editedSpecification" ' + \
'style="height:1000vh" spellcheck="true" ' + \
'autocomplete="on">' + specification_str + '</textarea>'
edit_links_form += \
'</div>'
edit_links_form += html_footer()
return edit_links_form

View File

@ -102,7 +102,7 @@ def _html_podcast_chapters(link_url: str,
if chapters_html:
html_str = \
'<div class="chapters">\n' + \
' <ul>\n' + chapters_html + ' </ul>\n</div>\n'
' <u>\n' + chapters_html + ' </u>\n</div>\n'
return html_str

View File

@ -1008,7 +1008,7 @@ def _get_blog_citations_html(box_name: str,
if translate.get(translated_citations_str):
translated_citations_str = translate[translated_citations_str]
citations_str = '<p><b>' + translated_citations_str + ':</b></p>' + \
'<ul>\n' + citations_str + '</ul>\n'
'<u>\n' + citations_str + '</u>\n'
return citations_str

View File

@ -0,0 +1,69 @@
__filename__ = "webapp_about.py"
__author__ = "Bob Mottram"
__license__ = "AGPL3+"
__version__ = "1.3.0"
__maintainer__ = "Bob Mottram"
__email__ = "bob@libreserver.org"
__status__ = "Production"
__module_group__ = "Web Interface"
import os
from shutil import copyfile
from utils import get_config_param
from webapp_utils import html_header_with_website_markup
from webapp_utils import html_footer
from markdown import markdown_example_numbers
from markdown import markdown_to_html
def html_specification(css_cache: {}, base_dir: str, http_prefix: str,
domain_full: str, onion_domain: str, translate: {},
system_language: str) -> str:
"""Show the specification screen
"""
specification_filename = base_dir + '/specification/activitypub.md'
admin_nickname = get_config_param(base_dir, 'admin')
if os.path.isfile(base_dir + '/accounts/activitypub.md'):
specification_filename = base_dir + '/accounts/activitypub.md'
if os.path.isfile(base_dir + '/accounts/login-background-custom.jpg'):
if not os.path.isfile(base_dir + '/accounts/login-background.jpg'):
copyfile(base_dir + '/accounts/login-background-custom.jpg',
base_dir + '/accounts/login-background.jpg')
specification_text = 'ActivityPub Protocol Specification.'
if os.path.isfile(specification_filename):
with open(specification_filename, 'r',
encoding='utf-8') as fp_specification:
md_text = markdown_example_numbers(fp_specification.read())
specification_text = markdown_to_html(md_text)
specification_form = ''
css_filename = base_dir + '/epicyon-profile.css'
if os.path.isfile(base_dir + '/epicyon.css'):
css_filename = base_dir + '/epicyon.css'
instance_title = \
get_config_param(base_dir, 'instanceTitle')
specification_form = \
html_header_with_website_markup(css_filename, instance_title,
http_prefix, domain_full,
system_language)
specification_form += \
'<div class="container">' + specification_text + '</div>'
if onion_domain:
specification_form += \
'<div class="container"><center>\n' + \
'<p class="administeredby">' + \
'http://' + onion_domain + '</p>\n</center></div>\n'
if admin_nickname:
admin_actor = '/users/' + admin_nickname
specification_form += \
'<div class="container"><center>\n' + \
'<p class="administeredby">' + \
translate['Administered by'] + ' <a href="' + \
admin_actor + '">' + admin_nickname + '</a>. ' + \
translate['Version'] + ' ' + __version__ + \
'</p>\n</center></div>\n'
specification_form += html_footer()
return specification_form