Update.
This commit is contained in:
parent
f69586ccf7
commit
bca39a612c
@ -23,6 +23,7 @@ dependencies = [
|
|||||||
"wkhtmltopdf",
|
"wkhtmltopdf",
|
||||||
"mistune",
|
"mistune",
|
||||||
"aiohttp-session",
|
"aiohttp-session",
|
||||||
"cryptography"
|
"cryptography",
|
||||||
|
"requests"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ from snek.system import http
|
|||||||
from snek.system.cache import Cache
|
from snek.system.cache import Cache
|
||||||
from snek.system.markdown import MarkdownExtension
|
from snek.system.markdown import MarkdownExtension
|
||||||
from snek.system.middleware import cors_middleware
|
from snek.system.middleware import cors_middleware
|
||||||
|
from snek.system.template import LinkifyExtension, PythonExtension
|
||||||
from snek.view.about import AboutHTMLView, AboutMDView
|
from snek.view.about import AboutHTMLView, AboutMDView
|
||||||
from snek.view.docs import DocsHTMLView, DocsMDView
|
from snek.view.docs import DocsHTMLView, DocsMDView
|
||||||
from snek.view.index import IndexView
|
from snek.view.index import IndexView
|
||||||
@ -51,6 +52,9 @@ class Application(BaseApplication):
|
|||||||
session_setup(self, EncryptedCookieStorage(SESSION_KEY))
|
session_setup(self, EncryptedCookieStorage(SESSION_KEY))
|
||||||
self._middlewares.append(session_middleware)
|
self._middlewares.append(session_middleware)
|
||||||
self.jinja2_env.add_extension(MarkdownExtension)
|
self.jinja2_env.add_extension(MarkdownExtension)
|
||||||
|
self.jinja2_env.add_extension(LinkifyExtension)
|
||||||
|
self.jinja2_env.add_extension(PythonExtension)
|
||||||
|
|
||||||
self.setup_router()
|
self.setup_router()
|
||||||
self.cache = Cache(self)
|
self.cache = Cache(self)
|
||||||
self.services = get_services(app=self)
|
self.services = get_services(app=self)
|
||||||
|
@ -209,6 +209,11 @@ message-list {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.message {
|
.message {
|
||||||
|
max-width: 100%;
|
||||||
|
word-wrap: break-word;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
hyphens: auto;
|
||||||
|
|
||||||
.avatar {
|
.avatar {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
99
src/snek/system/template.py
Normal file
99
src/snek/system/template.py
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
from types import SimpleNamespace
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def set_link_target_blank(text):
|
||||||
|
soup = BeautifulSoup(text, 'html.parser')
|
||||||
|
|
||||||
|
for element in soup.find_all("a"):
|
||||||
|
element.attrs['target'] = '_blank'
|
||||||
|
element.attrs['rel'] = 'noopener noreferrer'
|
||||||
|
element.attrs['referrerpolicy'] = 'no-referrer'
|
||||||
|
|
||||||
|
return str(soup)
|
||||||
|
|
||||||
|
|
||||||
|
def linkify_https(text):
|
||||||
|
url_pattern = r'(?<!["\'])\bhttps://[^\s<>()]+'
|
||||||
|
|
||||||
|
soup = BeautifulSoup(text, 'html.parser')
|
||||||
|
|
||||||
|
for element in soup.find_all(text=True):
|
||||||
|
parent = element.parent
|
||||||
|
if parent.name in ['a', 'script', 'style']:
|
||||||
|
continue
|
||||||
|
|
||||||
|
new_text = re.sub(url_pattern, r'<a href="\g<0>">\g<0></a>', element)
|
||||||
|
element.replace_with(BeautifulSoup(new_text, 'html.parser'))
|
||||||
|
|
||||||
|
return set_link_target_blank(str(soup))
|
||||||
|
|
||||||
|
|
||||||
|
from jinja2 import TemplateSyntaxError, nodes
|
||||||
|
from jinja2.ext import Extension
|
||||||
|
from jinja2.nodes import Const
|
||||||
|
|
||||||
|
|
||||||
|
class LinkifyExtension(Extension):
|
||||||
|
tags = {"linkify"}
|
||||||
|
|
||||||
|
def __init__(self, environment):
|
||||||
|
self.app = SimpleNamespace(jinja2_env=environment)
|
||||||
|
super(LinkifyExtension, self).__init__(environment)
|
||||||
|
|
||||||
|
def parse(self, parser):
|
||||||
|
line_number = next(parser.stream).lineno
|
||||||
|
md_file = [Const("")]
|
||||||
|
body = ""
|
||||||
|
try:
|
||||||
|
md_file = [parser.parse_expression()]
|
||||||
|
except TemplateSyntaxError:
|
||||||
|
body = parser.parse_statements(["name:endlinkify"], drop_needle=True)
|
||||||
|
return nodes.CallBlock(
|
||||||
|
self.call_method("_to_html", md_file), [], [], body
|
||||||
|
).set_lineno(line_number)
|
||||||
|
|
||||||
|
def _to_html(self, md_file, caller):
|
||||||
|
return linkify_https(caller())
|
||||||
|
|
||||||
|
class PythonExtension(Extension):
|
||||||
|
tags = {"py3"}
|
||||||
|
|
||||||
|
def parse(self, parser):
|
||||||
|
line_number = next(parser.stream).lineno
|
||||||
|
md_file = [Const("")]
|
||||||
|
body = ""
|
||||||
|
try:
|
||||||
|
md_file = [parser.parse_expression()]
|
||||||
|
except TemplateSyntaxError:
|
||||||
|
body = parser.parse_statements(["name:endpy3"], drop_needle=True)
|
||||||
|
return nodes.CallBlock(
|
||||||
|
self.call_method("_to_html", md_file), [], [], body
|
||||||
|
).set_lineno(line_number)
|
||||||
|
|
||||||
|
def _to_html(self, md_file, caller):
|
||||||
|
|
||||||
|
def fn(source):
|
||||||
|
import subprocess
|
||||||
|
import subprocess
|
||||||
|
import pathlib
|
||||||
|
from pathlib import Path
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import requests
|
||||||
|
def system(command):
|
||||||
|
if isinstance(command):
|
||||||
|
command = command.split(" ")
|
||||||
|
from io import StringIO
|
||||||
|
stdout = StringIO()
|
||||||
|
subprocess.run(command,stderr=stdout,stdout=stdout,text=True)
|
||||||
|
return stdout.getvalue()
|
||||||
|
to_write = []
|
||||||
|
def render(text):
|
||||||
|
global to_write
|
||||||
|
to_write.append(text)
|
||||||
|
exec(source)
|
||||||
|
return "".join(to_write)
|
||||||
|
return str(fn(caller()))
|
@ -1,10 +1,14 @@
|
|||||||
<style>
|
<style>
|
||||||
{{highlight_styles}}
|
{{highlight_styles}}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
{#
|
{#
|
||||||
<div data-uid="{{uid}}" data-color="{{color}}" data-channel_uid="{{channel_uid}}" data-user_nick="{{user_nick}}" data-created_at="{{created_at}}" data-user_uid="{{user_uid}}" data-message="{{message}}" class="message"><div class="avatar" style="background-color: {{color}}; color: black;">{{user_nick[0]}}</div><div class="message-content"><div class="author" style="color: {{color}};">{{user_nick}}</div><div class="text">
|
<div style="max-width:100%;" data-uid="{{uid}}" data-color="{{color}}" data-channel_uid="{{channel_uid}}" data-user_nick="{{user_nick}}" data-created_at="{{created_at}}" data-user_uid="{{user_uid}}" data-message="{{message}}" class="message"><div class="avatar" style="background-color: {{color}}; color: black;">{{user_nick[0]}}</div><div class="message-content"><div class="author" style="color: {{color}};">{{user_nick}}</div><div class="text">
|
||||||
#}
|
#}
|
||||||
|
{% linkify %}
|
||||||
|
|
||||||
{% markdown %}{% autoescape false %}{{ message }}{%raw %} {% endraw%}{%endautoescape%}{% endmarkdown %}
|
{% markdown %}{% autoescape false %}{{ message }}{%raw %} {% endraw%}{%endautoescape%}{% endmarkdown %}
|
||||||
|
{% endlinkify %}
|
||||||
{#
|
{#
|
||||||
</div><div class="time">{{created_at}}</div></div></div>
|
</div><div class="time">{{created_at}}</div></div></div>
|
||||||
#}
|
#}
|
Loading…
Reference in New Issue
Block a user