mirror of https://gitlab.com/bashrc2/epicyon
Merge branch 'main' of gitlab.com:bashrc2/epicyon
commit
aead5d2cf9
2
blog.py
2
blog.py
|
@ -313,7 +313,7 @@ def _html_blog_post_content(debug: bool, session, authorized: bool,
|
||||||
if citations_str:
|
if citations_str:
|
||||||
citations_str = '<p><b>' + translate['Citations'] + \
|
citations_str = '<p><b>' + translate['Citations'] + \
|
||||||
':</b></p>' + \
|
':</b></p>' + \
|
||||||
'<ul>\n' + citations_str + '</ul>\n'
|
'<u>\n' + citations_str + '</u>\n'
|
||||||
|
|
||||||
blog_str += '<br>\n' + citations_str
|
blog_str += '<br>\n' + citations_str
|
||||||
|
|
||||||
|
|
110
daemon.py
110
daemon.py
|
@ -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_delete_confirm
|
||||||
from webapp_calendar import html_calendar
|
from webapp_calendar import html_calendar
|
||||||
from webapp_about import html_about
|
from webapp_about import html_about
|
||||||
|
from webapp_specification import html_specification
|
||||||
from webapp_accesskeys import html_access_keys
|
from webapp_accesskeys import html_access_keys
|
||||||
from webapp_accesskeys import load_access_keys_for_accounts
|
from webapp_accesskeys import load_access_keys_for_accounts
|
||||||
from webapp_confirm import html_confirm_delete
|
from webapp_confirm import html_confirm_delete
|
||||||
|
@ -4782,6 +4783,7 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
links_filename = base_dir + '/accounts/links.txt'
|
links_filename = base_dir + '/accounts/links.txt'
|
||||||
about_filename = base_dir + '/accounts/about.md'
|
about_filename = base_dir + '/accounts/about.md'
|
||||||
tos_filename = base_dir + '/accounts/tos.md'
|
tos_filename = base_dir + '/accounts/tos.md'
|
||||||
|
specification_filename = base_dir + '/accounts/activitypub.md'
|
||||||
|
|
||||||
# extract all of the text fields into a dict
|
# extract all of the text fields into a dict
|
||||||
fields = \
|
fields = \
|
||||||
|
@ -4860,6 +4862,23 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
print('EX: _links_update unable to delete ' +
|
print('EX: _links_update unable to delete ' +
|
||||||
tos_filename)
|
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
|
# redirect back to the default timeline
|
||||||
self._redirect_headers(actor_str + '/' + default_timeline,
|
self._redirect_headers(actor_str + '/' + default_timeline,
|
||||||
cookie, calling_domain)
|
cookie, calling_domain)
|
||||||
|
@ -8212,6 +8231,56 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
return
|
return
|
||||||
self._404()
|
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,
|
def _show_help_screen_image(self, calling_domain: str, path: str,
|
||||||
base_dir: str, getreq_start_time) -> None:
|
base_dir: str, getreq_start_time) -> None:
|
||||||
"""Shows a help screen image
|
"""Shows a help screen image
|
||||||
|
@ -16274,6 +16343,39 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self.server.debug)
|
self.server.debug)
|
||||||
return
|
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 \
|
if html_getreq and users_in_path and authorized and \
|
||||||
self.path.endswith('/accesskeys'):
|
self.path.endswith('/accesskeys'):
|
||||||
nickname = self.path.split('/users/')[1]
|
nickname = self.path.split('/users/')[1]
|
||||||
|
@ -16683,6 +16785,14 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self.server.base_dir, getreq_start_time)
|
self.server.base_dir, getreq_start_time)
|
||||||
return
|
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
|
# help screen images
|
||||||
# Note that this comes before the busy flag to avoid conflicts
|
# Note that this comes before the busy flag to avoid conflicts
|
||||||
if self.path.startswith('/helpimages/'):
|
if self.path.startswith('/helpimages/'):
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -21,6 +21,7 @@
|
||||||
--header-bg-color: #282c37;
|
--header-bg-color: #282c37;
|
||||||
--main-bg-color: #282c37;
|
--main-bg-color: #282c37;
|
||||||
--post-bg-color: #282c37;
|
--post-bg-color: #282c37;
|
||||||
|
--code-color: lightblue;
|
||||||
--column-left-color: #282c37;
|
--column-left-color: #282c37;
|
||||||
--link-bg-color: #282c37;
|
--link-bg-color: #282c37;
|
||||||
--dropdown-fg-color: #dddddd;
|
--dropdown-fg-color: #dddddd;
|
||||||
|
@ -127,6 +128,7 @@
|
||||||
--quote-font-size-mobile: 120%;
|
--quote-font-size-mobile: 120%;
|
||||||
--quote-font-size-tiny: 60%;
|
--quote-font-size-tiny: 60%;
|
||||||
--line-spacing: 180%;
|
--line-spacing: 180%;
|
||||||
|
--code-spacing: 100%;
|
||||||
--line-spacing-newswire: 120%;
|
--line-spacing-newswire: 120%;
|
||||||
--newswire-item-moderated-color: white;
|
--newswire-item-moderated-color: white;
|
||||||
--newswire-date-moderated-color: white;
|
--newswire-date-moderated-color: white;
|
||||||
|
@ -248,6 +250,16 @@ body, html {
|
||||||
image-rendering: var(--rendering);
|
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 {
|
audio {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
330
markdown.py
330
markdown.py
|
@ -8,6 +8,38 @@ __status__ = "Production"
|
||||||
__module_group__ = "Web Interface"
|
__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:
|
def _markdown_emphasis_html(markdown: str) -> str:
|
||||||
"""Add italics and bold html markup to the given markdown
|
"""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>:',
|
||||||
'**;': '</b>;',
|
'**;': '</b>;',
|
||||||
|
'?**': '?</b>',
|
||||||
|
'\n**': '\n<b>',
|
||||||
'**,': '</b>,',
|
'**,': '</b>,',
|
||||||
'**\n': '</b>\n',
|
'**\n': '</b>\n',
|
||||||
|
'(**': '(<b>)',
|
||||||
|
'**)': '</b>)',
|
||||||
|
'>**': '><b>',
|
||||||
|
'**<': '</b><',
|
||||||
|
'>*': '><i>',
|
||||||
|
'*<': '</i><',
|
||||||
' *': ' <i>',
|
' *': ' <i>',
|
||||||
'* ': '</i> ',
|
'* ': '</i> ',
|
||||||
|
'?*': '?</i>',
|
||||||
|
'\n*': '\n<i>',
|
||||||
'*.': '</i>.',
|
'*.': '</i>.',
|
||||||
'*:': '</i>:',
|
'*:': '</i>:',
|
||||||
'*;': '</i>;',
|
'*;': '</i>;',
|
||||||
|
'(*': '(<i>)',
|
||||||
|
'*)': '</i>)',
|
||||||
'*,': '</i>,',
|
'*,': '</i>,',
|
||||||
'*\n': '</i>\n',
|
'*\n': '</i>\n',
|
||||||
' _': ' <ul>',
|
'(_': '(<u>',
|
||||||
'_ ': '</ul> ',
|
'_)': '</u>)',
|
||||||
'_.': '</ul>.',
|
' _': ' <u>',
|
||||||
'_:': '</ul>:',
|
'_ ': '</u> ',
|
||||||
'_;': '</ul>;',
|
'_.': '</u>.',
|
||||||
'_,': '</ul>,',
|
'_:': '</u>:',
|
||||||
'_\n': '</ul>\n'
|
'_;': '</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('**'):
|
sections = _markdown_get_sections(markdown)
|
||||||
markdown = markdown[2:] + '<b>'
|
markdown = ''
|
||||||
elif markdown.startswith('*'):
|
for section_text in sections:
|
||||||
markdown = markdown[1:] + '<i>'
|
if '<code>' in section_text:
|
||||||
elif markdown.startswith('_'):
|
markdown += section_text
|
||||||
markdown = markdown[1:] + '<ul>'
|
continue
|
||||||
|
for md_str, html in replacements.items():
|
||||||
|
section_text = section_text.replace(md_str, html)
|
||||||
|
|
||||||
if markdown.endswith('**'):
|
if section_text.startswith('**'):
|
||||||
markdown = markdown[:len(markdown) - 2] + '</b>'
|
section_text = section_text[2:] + '<b>'
|
||||||
elif markdown.endswith('*'):
|
elif section_text.startswith('*'):
|
||||||
markdown = markdown[:len(markdown) - 1] + '</i>'
|
section_text = section_text[1:] + '<i>'
|
||||||
elif markdown.endswith('_'):
|
elif section_text.startswith('_'):
|
||||||
markdown = markdown[:len(markdown) - 1] + '</ul>'
|
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
|
return markdown
|
||||||
|
|
||||||
|
|
||||||
|
@ -61,7 +127,19 @@ def _markdown_replace_quotes(markdown: str) -> str:
|
||||||
lines = markdown.split('\n')
|
lines = markdown.split('\n')
|
||||||
result = ''
|
result = ''
|
||||||
prev_quote_line = None
|
prev_quote_line = None
|
||||||
|
code_section = False
|
||||||
for line in lines:
|
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:
|
if '> ' not in line:
|
||||||
result += line + '\n'
|
result += line + '\n'
|
||||||
prev_quote_line = None
|
prev_quote_line = None
|
||||||
|
@ -90,56 +168,176 @@ def _markdown_replace_quotes(markdown: str) -> str:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
def _markdown_replace_links(markdown: str, images: bool = False) -> str:
|
def _markdown_replace_links(markdown: str) -> str:
|
||||||
"""Replaces markdown links with html
|
"""Replaces markdown links with html
|
||||||
Optionally replace image links
|
Optionally replace image links
|
||||||
"""
|
"""
|
||||||
replace_links = {}
|
sections = _markdown_get_sections(markdown)
|
||||||
text = markdown
|
result = ''
|
||||||
start_chars = '['
|
for section_text in sections:
|
||||||
if images:
|
if '<code>' in section_text or \
|
||||||
start_chars = '' 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]
|
|
||||||
continue
|
continue
|
||||||
if not images:
|
sections_links = section_text.split('](')
|
||||||
replace_links[markdown_link] = \
|
ctr = 0
|
||||||
'<a href="' + \
|
for link_section in sections_links:
|
||||||
markdown_link.split('(')[1].split(')')[0] + \
|
if ctr == 0:
|
||||||
'" target="_blank" rel="nofollow noopener noreferrer">' + \
|
ctr += 1
|
||||||
markdown_link.split(start_chars)[1].split(']')[0] + \
|
continue
|
||||||
'</a>'
|
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:
|
else:
|
||||||
replace_links[markdown_link] = \
|
if '</code>' in line:
|
||||||
'<img class="markdownImage" src="' + \
|
code_section = False
|
||||||
markdown_link.split('(')[1].split(')')[0] + \
|
if code_section:
|
||||||
'" alt="' + \
|
line_ctr += 1
|
||||||
markdown_link.split(start_chars)[1].split(']')[0] + \
|
continue
|
||||||
'" />'
|
|
||||||
text = text.split(')', 1)[1]
|
if not bullet_matched:
|
||||||
for md_link, html_link in replace_links.items():
|
for test_str in bullet_style:
|
||||||
markdown = markdown.replace(md_link, html_link)
|
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
|
return markdown
|
||||||
|
|
||||||
|
|
||||||
def markdown_to_html(markdown: str) -> str:
|
def markdown_to_html(markdown: str) -> str:
|
||||||
"""Converts markdown formatted text to html
|
"""Converts markdown formatted text to html
|
||||||
"""
|
"""
|
||||||
|
markdown = _markdown_replace_code(markdown)
|
||||||
|
markdown = _markdown_replace_bullet_points(markdown)
|
||||||
markdown = _markdown_replace_quotes(markdown)
|
markdown = _markdown_replace_quotes(markdown)
|
||||||
markdown = _markdown_emphasis_html(markdown)
|
markdown = _markdown_emphasis_html(markdown)
|
||||||
markdown = _markdown_replace_links(markdown, True)
|
|
||||||
markdown = _markdown_replace_links(markdown)
|
markdown = _markdown_replace_links(markdown)
|
||||||
|
|
||||||
# replace headers
|
# replace headers
|
||||||
lines_list = markdown.split('\n')
|
lines_list = markdown.split('\n')
|
||||||
html_str = ''
|
html_str = ''
|
||||||
ctr = 0
|
ctr = 0
|
||||||
|
code_section = False
|
||||||
titles = {
|
titles = {
|
||||||
"h5": '#####',
|
"h5": '#####',
|
||||||
"h4": '####',
|
"h4": '####',
|
||||||
|
@ -149,13 +347,37 @@ def markdown_to_html(markdown: str) -> str:
|
||||||
}
|
}
|
||||||
for line in lines_list:
|
for line in lines_list:
|
||||||
if ctr > 0:
|
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():
|
for hsh, hashes in titles.items():
|
||||||
if line.startswith(hashes):
|
if line.startswith(hashes):
|
||||||
line = line.replace(hashes, '').strip()
|
line = line.replace(hashes, '').strip()
|
||||||
line = '<' + hsh + '>' + line + '</' + hsh + '>'
|
line = '<' + hsh + '>' + line + '</' + hsh + '>\n'
|
||||||
ctr = -1
|
ctr = -1
|
||||||
break
|
break
|
||||||
html_str += line
|
html_str += line
|
||||||
ctr += 1
|
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
|
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
75
tests.py
75
tests.py
|
@ -5715,16 +5715,68 @@ def _test_markdown_to_html():
|
||||||
|
|
||||||
markdown = 'This is a quotation:\n' + \
|
markdown = 'This is a quotation:\n' + \
|
||||||
'> Some quote or other'
|
'> 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>'
|
'<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' + \
|
markdown = 'This is a multi-line quotation:\n' + \
|
||||||
'> The first line\n' + \
|
'> The first line\n' + \
|
||||||
'> The second line'
|
'> The second line'
|
||||||
assert markdown_to_html(markdown) == \
|
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>'
|
'<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**'
|
markdown = 'This is **bold**'
|
||||||
assert markdown_to_html(markdown) == 'This is <b>bold</b>'
|
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>'
|
assert markdown_to_html(markdown) == 'This is <i>italic</i>'
|
||||||
|
|
||||||
markdown = 'This is _underlined_'
|
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'
|
markdown = 'This is **just** plain text'
|
||||||
assert markdown_to_html(markdown) == 'This is <b>just</b> plain text'
|
assert markdown_to_html(markdown) == 'This is <b>just</b> plain text'
|
||||||
|
|
||||||
markdown = '# Title1\n### Title3\n## Title2\n'
|
markdown = '# Title1\n### Title3\n## Title2\n'
|
||||||
assert markdown_to_html(markdown) == \
|
expected = '<h1>Title1</h1>\n<h3>Title3</h3>\n<h2>Title2</h2>\n'
|
||||||
'<h1>Title1</h1><h3>Title3</h3><h2>Title2</h2>'
|
result = markdown_to_html(markdown)
|
||||||
|
if result != expected:
|
||||||
|
print(result)
|
||||||
|
assert result == expected
|
||||||
|
|
||||||
markdown = \
|
markdown = \
|
||||||
'This is [a link](https://something.somewhere) to something.\n' + \
|
'This is [a link](https://something.somewhere) to something.\n' + \
|
||||||
'And [something else](https://cat.pic).\n' + \
|
'And [something else](https://cat.pic).\n' + \
|
||||||
'Or .'
|
'Or .'
|
||||||
assert markdown_to_html(markdown) == \
|
expected = \
|
||||||
'This is <a href="https://something.somewhere" ' + \
|
'This is <a href="https://something.somewhere" ' + \
|
||||||
'target="_blank" rel="nofollow noopener noreferrer">' + \
|
'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" ' + \
|
'And <a href="https://cat.pic" ' + \
|
||||||
'target="_blank" rel="nofollow noopener noreferrer">' + \
|
'target="_blank" rel="nofollow noopener noreferrer">' + \
|
||||||
'something else</a>.<br>' + \
|
'something else</a>.<br>\n' + \
|
||||||
'Or <img class="markdownImage" src="/cat.jpg" alt="pounce" />.'
|
'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():
|
def _test_extract_text_fields_from_post():
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"code-color": "white",
|
||||||
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
|
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
|
||||||
"pwa-theme-background-color": "black-translucent",
|
"pwa-theme-background-color": "black-translucent",
|
||||||
"newswire-publish-icon": "True",
|
"newswire-publish-icon": "True",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"code-color": "blue",
|
||||||
"diff-add": "#111",
|
"diff-add": "#111",
|
||||||
"diff-remove": "#333",
|
"diff-remove": "#333",
|
||||||
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
|
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
"liker-names-margin": "2%",
|
"liker-names-margin": "2%",
|
||||||
"liker-names-vertical-spacing1": "50px",
|
"liker-names-vertical-spacing1": "50px",
|
||||||
"liker-names-vertical-spacing2": "100px",
|
"liker-names-vertical-spacing2": "100px",
|
||||||
|
"code-color": "lightblue",
|
||||||
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
|
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
|
||||||
"pwa-theme-background-color": "black-translucent",
|
"pwa-theme-background-color": "black-translucent",
|
||||||
"avatar-rounding": "10%",
|
"avatar-rounding": "10%",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"code-color": "lightblue",
|
||||||
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
|
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
|
||||||
"pwa-theme-background-color": "black-translucent",
|
"pwa-theme-background-color": "black-translucent",
|
||||||
"dropdown-fg-color": "#9ad791",
|
"dropdown-fg-color": "#9ad791",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"code-color": "blue",
|
||||||
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
|
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
|
||||||
"pwa-theme-background-color": "black-translucent",
|
"pwa-theme-background-color": "black-translucent",
|
||||||
"dropdown-fg-color": "white",
|
"dropdown-fg-color": "white",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"code-color": "lightblue",
|
||||||
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
|
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
|
||||||
"pwa-theme-background-color": "black-translucent",
|
"pwa-theme-background-color": "black-translucent",
|
||||||
"dropdown-fg-color": "white",
|
"dropdown-fg-color": "white",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"code-color": "blue",
|
||||||
"diff-add": "#111",
|
"diff-add": "#111",
|
||||||
"diff-remove": "#333",
|
"diff-remove": "#333",
|
||||||
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
|
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"code-color": "blue",
|
||||||
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
|
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
|
||||||
"pwa-theme-background-color": "black-translucent",
|
"pwa-theme-background-color": "black-translucent",
|
||||||
"dropdown-fg-color": "#33390d",
|
"dropdown-fg-color": "#33390d",
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
{
|
{
|
||||||
"diff-add": "#111",
|
"diff-add": "#111",
|
||||||
"diff-remove": "#333",
|
"diff-remove": "#333",
|
||||||
|
"code-color": "blue",
|
||||||
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
|
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
|
||||||
"pwa-theme-background-color": "black-translucent",
|
"pwa-theme-background-color": "black-translucent",
|
||||||
"dropdown-fg-color": "#2d2c37",
|
"dropdown-fg-color": "#2d2c37",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"code-color": "lightblue",
|
||||||
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
|
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
|
||||||
"pwa-theme-background-color": "black-translucent",
|
"pwa-theme-background-color": "black-translucent",
|
||||||
"dropdown-fg-color": "#0481f5",
|
"dropdown-fg-color": "#0481f5",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"code-color": "blue",
|
||||||
"diff-add": "#111",
|
"diff-add": "#111",
|
||||||
"diff-remove": "#333",
|
"diff-remove": "#333",
|
||||||
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
|
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"code-color": "lightblue",
|
||||||
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
|
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
|
||||||
"pwa-theme-background-color": "black-translucent",
|
"pwa-theme-background-color": "black-translucent",
|
||||||
"dropdown-fg-color": "#f98bb0",
|
"dropdown-fg-color": "#f98bb0",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"code-color": "lightblue",
|
||||||
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
|
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
|
||||||
"pwa-theme-background-color": "black-translucent",
|
"pwa-theme-background-color": "black-translucent",
|
||||||
"dropdown-fg-color": "white",
|
"dropdown-fg-color": "white",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"code-color": "blue",
|
||||||
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
|
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
|
||||||
"pwa-theme-background-color": "black-translucent",
|
"pwa-theme-background-color": "black-translucent",
|
||||||
"dropdown-fg-color": "#2d2c37",
|
"dropdown-fg-color": "#2d2c37",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"code-color": "lightblue",
|
||||||
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
|
"pwa-theme-color": "apple-mobile-web-app-status-bar-style",
|
||||||
"pwa-theme-background-color": "black-translucent",
|
"pwa-theme-background-color": "black-translucent",
|
||||||
"dropdown-fg-color": "#ffc4bc",
|
"dropdown-fg-color": "#ffc4bc",
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"code-color": "blue",
|
||||||
"font-size-likes-mobile": "64px",
|
"font-size-likes-mobile": "64px",
|
||||||
"likes-margin-left-mobile": "20px",
|
"likes-margin-left-mobile": "20px",
|
||||||
"likes-margin-right-mobile": "0px",
|
"likes-margin-right-mobile": "0px",
|
||||||
|
|
|
@ -564,5 +564,6 @@
|
||||||
"Save": "يحفظ",
|
"Save": "يحفظ",
|
||||||
"Switch to moderation view": "قم بالتبديل إلى عرض الاعتدال",
|
"Switch to moderation view": "قم بالتبديل إلى عرض الاعتدال",
|
||||||
"Minimize attached images": "تصغير الصور المرفقة",
|
"Minimize attached images": "تصغير الصور المرفقة",
|
||||||
"SHOW MEDIA": "عرض الوسائط"
|
"SHOW MEDIA": "عرض الوسائط",
|
||||||
|
"ActivityPub Specification": "مواصفات ActivityPub"
|
||||||
}
|
}
|
||||||
|
|
|
@ -564,5 +564,6 @@
|
||||||
"Save": "সংরক্ষণ",
|
"Save": "সংরক্ষণ",
|
||||||
"Switch to moderation view": "সংযম দৃশ্যে স্যুইচ করুন",
|
"Switch to moderation view": "সংযম দৃশ্যে স্যুইচ করুন",
|
||||||
"Minimize attached images": "সংযুক্ত ছবি ছোট করুন",
|
"Minimize attached images": "সংযুক্ত ছবি ছোট করুন",
|
||||||
"SHOW MEDIA": "মিডিয়া দেখান"
|
"SHOW MEDIA": "মিডিয়া দেখান",
|
||||||
|
"ActivityPub Specification": "ActivityPub স্পেসিফিকেশন"
|
||||||
}
|
}
|
||||||
|
|
|
@ -564,5 +564,6 @@
|
||||||
"Save": "Desa",
|
"Save": "Desa",
|
||||||
"Switch to moderation view": "Canvia a la visualització de moderació",
|
"Switch to moderation view": "Canvia a la visualització de moderació",
|
||||||
"Minimize attached images": "Minimitzar les imatges adjuntes",
|
"Minimize attached images": "Minimitzar les imatges adjuntes",
|
||||||
"SHOW MEDIA": "MOSTRA ELS MITJANS"
|
"SHOW MEDIA": "MOSTRA ELS MITJANS",
|
||||||
|
"ActivityPub Specification": "Especificació d'ActivityPub"
|
||||||
}
|
}
|
||||||
|
|
|
@ -564,5 +564,6 @@
|
||||||
"Save": "Arbed",
|
"Save": "Arbed",
|
||||||
"Switch to moderation view": "Newid i wedd safoni",
|
"Switch to moderation view": "Newid i wedd safoni",
|
||||||
"Minimize attached images": "Lleihau delweddau sydd ynghlwm",
|
"Minimize attached images": "Lleihau delweddau sydd ynghlwm",
|
||||||
"SHOW MEDIA": "DANGOS CYFRYNGAU"
|
"SHOW MEDIA": "DANGOS CYFRYNGAU",
|
||||||
|
"ActivityPub Specification": "Manyleb GweithgareddPub"
|
||||||
}
|
}
|
||||||
|
|
|
@ -564,5 +564,6 @@
|
||||||
"Save": "Speichern",
|
"Save": "Speichern",
|
||||||
"Switch to moderation view": "Wechseln Sie zur Moderationsansicht",
|
"Switch to moderation view": "Wechseln Sie zur Moderationsansicht",
|
||||||
"Minimize attached images": "Angehängte Bilder minimieren",
|
"Minimize attached images": "Angehängte Bilder minimieren",
|
||||||
"SHOW MEDIA": "MEDIEN ZEIGEN"
|
"SHOW MEDIA": "MEDIEN ZEIGEN",
|
||||||
|
"ActivityPub Specification": "ActivityPub-Spezifikation"
|
||||||
}
|
}
|
||||||
|
|
|
@ -564,5 +564,6 @@
|
||||||
"Save": "Αποθηκεύσετε",
|
"Save": "Αποθηκεύσετε",
|
||||||
"Switch to moderation view": "Μετάβαση σε προβολή εποπτείας",
|
"Switch to moderation view": "Μετάβαση σε προβολή εποπτείας",
|
||||||
"Minimize attached images": "Ελαχιστοποιήστε τις συνημμένες εικόνες",
|
"Minimize attached images": "Ελαχιστοποιήστε τις συνημμένες εικόνες",
|
||||||
"SHOW MEDIA": "ΔΕΙΤΕ ΜΕΣΑ"
|
"SHOW MEDIA": "ΔΕΙΤΕ ΜΕΣΑ",
|
||||||
|
"ActivityPub Specification": "Προδιαγραφές ActivityPub"
|
||||||
}
|
}
|
||||||
|
|
|
@ -564,5 +564,6 @@
|
||||||
"Save": "Save",
|
"Save": "Save",
|
||||||
"Switch to moderation view": "Switch to moderation view",
|
"Switch to moderation view": "Switch to moderation view",
|
||||||
"Minimize attached images": "Minimize attached images",
|
"Minimize attached images": "Minimize attached images",
|
||||||
"SHOW MEDIA": "SHOW MEDIA"
|
"SHOW MEDIA": "SHOW MEDIA",
|
||||||
|
"ActivityPub Specification": "ActivityPub Specification"
|
||||||
}
|
}
|
||||||
|
|
|
@ -564,5 +564,6 @@
|
||||||
"Save": "Ahorrar",
|
"Save": "Ahorrar",
|
||||||
"Switch to moderation view": "Cambiar a la vista de moderación",
|
"Switch to moderation view": "Cambiar a la vista de moderación",
|
||||||
"Minimize attached images": "Minimizar imágenes adjuntas",
|
"Minimize attached images": "Minimizar imágenes adjuntas",
|
||||||
"SHOW MEDIA": "MOSTRAR MEDIOS"
|
"SHOW MEDIA": "MOSTRAR MEDIOS",
|
||||||
|
"ActivityPub Specification": "Especificación de ActivityPub"
|
||||||
}
|
}
|
||||||
|
|
|
@ -564,5 +564,6 @@
|
||||||
"Save": "Sauvegarder",
|
"Save": "Sauvegarder",
|
||||||
"Switch to moderation view": "Passer en mode modération",
|
"Switch to moderation view": "Passer en mode modération",
|
||||||
"Minimize attached images": "Réduire les images jointes",
|
"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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -564,5 +564,6 @@
|
||||||
"Save": "Sábháil",
|
"Save": "Sábháil",
|
||||||
"Switch to moderation view": "Athraigh go dtí an t-amharc modhnóireachta",
|
"Switch to moderation view": "Athraigh go dtí an t-amharc modhnóireachta",
|
||||||
"Minimize attached images": "Íoslaghdaigh íomhánna ceangailte",
|
"Minimize attached images": "Íoslaghdaigh íomhánna ceangailte",
|
||||||
"SHOW MEDIA": "Taispeáin MEÁIN"
|
"SHOW MEDIA": "Taispeáin MEÁIN",
|
||||||
|
"ActivityPub Specification": "Sonraíocht ActivityPub"
|
||||||
}
|
}
|
||||||
|
|
|
@ -564,5 +564,6 @@
|
||||||
"Save": "बचाना",
|
"Save": "बचाना",
|
||||||
"Switch to moderation view": "मॉडरेशन दृश्य पर स्विच करें",
|
"Switch to moderation view": "मॉडरेशन दृश्य पर स्विच करें",
|
||||||
"Minimize attached images": "संलग्न छवियों को छोटा करें",
|
"Minimize attached images": "संलग्न छवियों को छोटा करें",
|
||||||
"SHOW MEDIA": "मीडिया दिखाएं"
|
"SHOW MEDIA": "मीडिया दिखाएं",
|
||||||
|
"ActivityPub Specification": "गतिविधिपब विशिष्टता"
|
||||||
}
|
}
|
||||||
|
|
|
@ -564,5 +564,6 @@
|
||||||
"Save": "Salva",
|
"Save": "Salva",
|
||||||
"Switch to moderation view": "Passa alla visualizzazione moderazione",
|
"Switch to moderation view": "Passa alla visualizzazione moderazione",
|
||||||
"Minimize attached images": "Riduci al minimo le immagini allegate",
|
"Minimize attached images": "Riduci al minimo le immagini allegate",
|
||||||
"SHOW MEDIA": "MOSTRA MEDIA"
|
"SHOW MEDIA": "MOSTRA MEDIA",
|
||||||
|
"ActivityPub Specification": "Specifica ActivityPub"
|
||||||
}
|
}
|
||||||
|
|
|
@ -564,5 +564,6 @@
|
||||||
"Save": "保存",
|
"Save": "保存",
|
||||||
"Switch to moderation view": "モデレートビューに切り替えます",
|
"Switch to moderation view": "モデレートビューに切り替えます",
|
||||||
"Minimize attached images": "添付画像を最小限に抑える",
|
"Minimize attached images": "添付画像を最小限に抑える",
|
||||||
"SHOW MEDIA": "メディアを表示"
|
"SHOW MEDIA": "メディアを表示",
|
||||||
|
"ActivityPub Specification": "ActivityPubの仕様"
|
||||||
}
|
}
|
||||||
|
|
|
@ -564,5 +564,6 @@
|
||||||
"Save": "구하다",
|
"Save": "구하다",
|
||||||
"Switch to moderation view": "검토 보기로 전환",
|
"Switch to moderation view": "검토 보기로 전환",
|
||||||
"Minimize attached images": "첨부된 이미지 최소화",
|
"Minimize attached images": "첨부된 이미지 최소화",
|
||||||
"SHOW MEDIA": "미디어 표시"
|
"SHOW MEDIA": "미디어 표시",
|
||||||
|
"ActivityPub Specification": "ActivityPub 사양"
|
||||||
}
|
}
|
||||||
|
|
|
@ -564,5 +564,6 @@
|
||||||
"Save": "Rizgarkirin",
|
"Save": "Rizgarkirin",
|
||||||
"Switch to moderation view": "Biguherîne bo dîtina moderatoriyê",
|
"Switch to moderation view": "Biguherîne bo dîtina moderatoriyê",
|
||||||
"Minimize attached images": "Wêneyên pêvekirî kêm bikin",
|
"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"
|
||||||
}
|
}
|
||||||
|
|
|
@ -564,5 +564,6 @@
|
||||||
"Save": "Opslaan",
|
"Save": "Opslaan",
|
||||||
"Switch to moderation view": "Overschakelen naar moderatieweergave",
|
"Switch to moderation view": "Overschakelen naar moderatieweergave",
|
||||||
"Minimize attached images": "Bijgevoegde afbeeldingen minimaliseren",
|
"Minimize attached images": "Bijgevoegde afbeeldingen minimaliseren",
|
||||||
"SHOW MEDIA": "TOON MEDIA"
|
"SHOW MEDIA": "TOON MEDIA",
|
||||||
|
"ActivityPub Specification": "ActivityPub-specificatie"
|
||||||
}
|
}
|
||||||
|
|
|
@ -560,5 +560,6 @@
|
||||||
"Save": "Save",
|
"Save": "Save",
|
||||||
"Switch to moderation view": "Switch to moderation view",
|
"Switch to moderation view": "Switch to moderation view",
|
||||||
"Minimize attached images": "Minimize attached images",
|
"Minimize attached images": "Minimize attached images",
|
||||||
"SHOW MEDIA": "SHOW MEDIA"
|
"SHOW MEDIA": "SHOW MEDIA",
|
||||||
|
"ActivityPub Specification": "ActivityPub Specification"
|
||||||
}
|
}
|
||||||
|
|
|
@ -564,5 +564,6 @@
|
||||||
"Save": "Ratować",
|
"Save": "Ratować",
|
||||||
"Switch to moderation view": "Przełącz na widok moderacji",
|
"Switch to moderation view": "Przełącz na widok moderacji",
|
||||||
"Minimize attached images": "Zminimalizuj załączone obrazy",
|
"Minimize attached images": "Zminimalizuj załączone obrazy",
|
||||||
"SHOW MEDIA": "POKAŻ MEDIA"
|
"SHOW MEDIA": "POKAŻ MEDIA",
|
||||||
|
"ActivityPub Specification": "Specyfikacja ActivityPub"
|
||||||
}
|
}
|
||||||
|
|
|
@ -564,5 +564,6 @@
|
||||||
"Save": "Salvar",
|
"Save": "Salvar",
|
||||||
"Switch to moderation view": "Mudar para a visualização de moderação",
|
"Switch to moderation view": "Mudar para a visualização de moderação",
|
||||||
"Minimize attached images": "Minimizar imagens anexadas",
|
"Minimize attached images": "Minimizar imagens anexadas",
|
||||||
"SHOW MEDIA": "MOSTRAR MÍDIA"
|
"SHOW MEDIA": "MOSTRAR MÍDIA",
|
||||||
|
"ActivityPub Specification": "Especificação do ActivityPub"
|
||||||
}
|
}
|
||||||
|
|
|
@ -564,5 +564,6 @@
|
||||||
"Save": "Сохранять",
|
"Save": "Сохранять",
|
||||||
"Switch to moderation view": "Перейти в режим модерации",
|
"Switch to moderation view": "Перейти в режим модерации",
|
||||||
"Minimize attached images": "Свернуть прикрепленные изображения",
|
"Minimize attached images": "Свернуть прикрепленные изображения",
|
||||||
"SHOW MEDIA": "ПОКАЗАТЬ МЕДИА"
|
"SHOW MEDIA": "ПОКАЗАТЬ МЕДИА",
|
||||||
|
"ActivityPub Specification": "Спецификация ActivityPub"
|
||||||
}
|
}
|
||||||
|
|
|
@ -564,5 +564,6 @@
|
||||||
"Save": "Hifadhi",
|
"Save": "Hifadhi",
|
||||||
"Switch to moderation view": "Badili hadi mwonekano wa udhibiti",
|
"Switch to moderation view": "Badili hadi mwonekano wa udhibiti",
|
||||||
"Minimize attached images": "Punguza picha zilizoambatishwa",
|
"Minimize attached images": "Punguza picha zilizoambatishwa",
|
||||||
"SHOW MEDIA": "ONESHA VYOMBO VYA HABARI"
|
"SHOW MEDIA": "ONESHA VYOMBO VYA HABARI",
|
||||||
|
"ActivityPub Specification": "Vipimo vya ActivityPub"
|
||||||
}
|
}
|
||||||
|
|
|
@ -564,5 +564,6 @@
|
||||||
"Save": "Kaydetmek",
|
"Save": "Kaydetmek",
|
||||||
"Switch to moderation view": "Denetleme görünümüne geç",
|
"Switch to moderation view": "Denetleme görünümüne geç",
|
||||||
"Minimize attached images": "Ekli resimleri simge durumuna küçült",
|
"Minimize attached images": "Ekli resimleri simge durumuna küçült",
|
||||||
"SHOW MEDIA": "MEDYA GÖSTER"
|
"SHOW MEDIA": "MEDYA GÖSTER",
|
||||||
|
"ActivityPub Specification": "ActivityPub Spesifikasyonu"
|
||||||
}
|
}
|
||||||
|
|
|
@ -564,5 +564,6 @@
|
||||||
"Save": "Зберегти",
|
"Save": "Зберегти",
|
||||||
"Switch to moderation view": "Перейти до режиму модерації",
|
"Switch to moderation view": "Перейти до режиму модерації",
|
||||||
"Minimize attached images": "Мінімізуйте вкладені зображення",
|
"Minimize attached images": "Мінімізуйте вкладені зображення",
|
||||||
"SHOW MEDIA": "ПОКАЗАТИ ЗМІ"
|
"SHOW MEDIA": "ПОКАЗАТИ ЗМІ",
|
||||||
|
"ActivityPub Specification": "Специфікація ActivityPub"
|
||||||
}
|
}
|
||||||
|
|
|
@ -564,5 +564,6 @@
|
||||||
"Save": "היט",
|
"Save": "היט",
|
||||||
"Switch to moderation view": "באַשטימען צו מאַדעריישאַן מיינונג",
|
"Switch to moderation view": "באַשטימען צו מאַדעריישאַן מיינונג",
|
||||||
"Minimize attached images": "מינאַמייז אַטאַטשט בילדער",
|
"Minimize attached images": "מינאַמייז אַטאַטשט בילדער",
|
||||||
"SHOW MEDIA": "ווייַז מעדיע"
|
"SHOW MEDIA": "ווייַז מעדיע",
|
||||||
|
"ActivityPub Specification": "ActivityPub באַשרייַבונג"
|
||||||
}
|
}
|
||||||
|
|
|
@ -564,5 +564,6 @@
|
||||||
"Save": "节省",
|
"Save": "节省",
|
||||||
"Switch to moderation view": "切换到审核视图",
|
"Switch to moderation view": "切换到审核视图",
|
||||||
"Minimize attached images": "最小化附加图像",
|
"Minimize attached images": "最小化附加图像",
|
||||||
"SHOW MEDIA": "展示媒体"
|
"SHOW MEDIA": "展示媒体",
|
||||||
|
"ActivityPub Specification": "ActivityPub 规范"
|
||||||
}
|
}
|
||||||
|
|
|
@ -346,6 +346,9 @@ def get_left_column_content(base_dir: str, nickname: str, domain_full: str,
|
||||||
html_str += \
|
html_str += \
|
||||||
'<p class="login-text"><a href="/about">' + \
|
'<p class="login-text"><a href="/about">' + \
|
||||||
translate['About this Instance'] + '</a></p>'
|
translate['About this Instance'] + '</a></p>'
|
||||||
|
html_str += \
|
||||||
|
'<p class="login-text"><a href="/activitypub">' + \
|
||||||
|
translate['ActivityPub Specification'] + '</a></p>'
|
||||||
html_str += \
|
html_str += \
|
||||||
'<p class="login-text"><a href="/terms">' + \
|
'<p class="login-text"><a href="/terms">' + \
|
||||||
translate['Terms of Service'] + '</a></p>'
|
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 += \
|
edit_links_form += \
|
||||||
'</div>'
|
'</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')
|
admin_nickname = get_config_param(base_dir, 'admin')
|
||||||
if admin_nickname:
|
if admin_nickname:
|
||||||
if nickname == 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 += \
|
edit_links_form += \
|
||||||
'</div>'
|
'</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()
|
edit_links_form += html_footer()
|
||||||
return edit_links_form
|
return edit_links_form
|
||||||
|
|
|
@ -102,7 +102,7 @@ def _html_podcast_chapters(link_url: str,
|
||||||
if chapters_html:
|
if chapters_html:
|
||||||
html_str = \
|
html_str = \
|
||||||
'<div class="chapters">\n' + \
|
'<div class="chapters">\n' + \
|
||||||
' <ul>\n' + chapters_html + ' </ul>\n</div>\n'
|
' <u>\n' + chapters_html + ' </u>\n</div>\n'
|
||||||
return html_str
|
return html_str
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1008,7 +1008,7 @@ def _get_blog_citations_html(box_name: str,
|
||||||
if translate.get(translated_citations_str):
|
if translate.get(translated_citations_str):
|
||||||
translated_citations_str = translate[translated_citations_str]
|
translated_citations_str = translate[translated_citations_str]
|
||||||
citations_str = '<p><b>' + translated_citations_str + ':</b></p>' + \
|
citations_str = '<p><b>' + translated_citations_str + ':</b></p>' + \
|
||||||
'<ul>\n' + citations_str + '</ul>\n'
|
'<u>\n' + citations_str + '</u>\n'
|
||||||
return citations_str
|
return citations_str
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
Loading…
Reference in New Issue