# Written by retoor@molodetz.nl
# This source code implements a web application using the aiohttp framework. It provides a template rendering system that supports both HTML and Markdown files. The `TemplateView` class handles both GET and POST requests, resolving and rendering templates based on request paths. The `Dreamii` class sets up the application environment, adding support for markdown and python extension within jinja2 templates.
# Imports:
# 1. `aiohttp_jinja2`, `web` are part of the aiohttp framework for creating web servers and managing web requests.
# 2. `MarkdownExtension` and `PythonExtension` are custom extensions that facilitate Markdown and Python rendering within templates.
# MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from app.app import Application as BaseApplication, BaseView
import pathlib
from aiohttp import web
from dreamii.markdown import MarkdownExtension, MarkdownRenderer, render_markdown_sync
from dreamii.python import PythonExtension
import aiohttp_jinja2
import html
class TemplateView(BaseView):
async def resolve_template(self, path):
path = pathlib.Path(self.request.app.template_path).joinpath(str(path).lstrip('/'))
if path.exists() and path.suffix in ['.html', '.md']:
return str(path)
elif path.joinpath('.md').exists():
return str(path.joinpath('.md'))
elif path.joinpath('.html').exists():
return str(path.joinpath('.html'))
elif path.is_dir():
if path.joinpath('index.html').exists():
return str(path.joinpath('index.html'))
elif path.joinpath('index.md').exists():
return str(path.joinpath('index.md'))
return None
async def render_template(self, path):
context = {}
context['request'] = self.request
context['post'] = await self.request.post()
context["template"] = pathlib.Path(path)
if not context['post']:
context['post'] = None
try:
context['json'] = await self.request.json()
except:
context['json'] = None
pass
self.request.app.jinja2_env.globals["request"] = self.request
self.request.app.jinja2_env.globals["context"] = context
self.request.app.jinja2_env.globals["template"] = template
if str(path).endswith(".md"):
renderer = MarkdownRenderer(self.request.app, path)
content = pathlib.Path(path).read_text()
if pathlib.Path(self.template_path).joinpath("_base.html").exists():
markdown_default_page = "{% extends \"_base.html\" %}<html>{% block content %}<style>{{ highlight_styles }}</style>{% markdown %}"+content+"{% endmarkdown %}{% endblock %}</body></html>"
else:
markdown_default_page = "\n".join([
"<html>",
"<head>",
"<meta charset=\"utf-8\">",
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">",
"<title>" + html.escape(path.split("/")[-1].rstrip(".md")) +"</title>",
"<style>{{ highlight_styles }}</style>",
"</head>",
"<body>{% markdown %}"+content+"{% endmarkdown %}</body>",
"</html>"
])
with open(".temp.html", "w+") as f:
f.write(markdown_default_page)
content = aiohttp_jinja2.render_string(".temp.html",self.request,context=context)
pathlib.Path(".temp.html").unlink()
response = web.Response(text=content, content_type="text/html")
return response
return await super().render_template(path)
async def get(self):
path = await self.resolve_template(self.request.match_info['tail'])
if path:
print("Found path", path)
return await self.render_template(path)
path = pathlib.Path(self.request.app.template_path).joinpath(self.request.match_info['tail'].lstrip('/'))
if path.exists():
print("Found non template path", path)
return web.Response(body=path.read_bytes())
print("Path not found", path)
return web.Response(status=404)
async def post(self):
path = await self.resolve_template(self.request.match_info['tail'])
if path:
return await self.render_template(path)
path = pathlib.Path(self.request.app.template_path).joinpath(self.request.match_info['tail'].lstrip('/'))
if path.exists():
return web.Response(body=path.read_bytes())
return web.Response(status=404)
class Dreamii(BaseApplication):
def __init__(self, *args, **kwargs):
super().__init__(template_path=".", *args, **kwargs)
self.router.add_view("/{tail:.*}", TemplateView)
self.jinja2_env.enable_async = False
self.jinja2_env.globals["app"] = self
self.jinja2_env.globals["request"] = None
self.jinja2_env.globals["context"] = {}
self.jinja2_env.add_extension(MarkdownExtension)
self.jinja2_env.add_extension(PythonExtension)
def run(self, port=7331):
web.run_app(self, port=port)
if __name__ == '__main__':
app = Dreamii()
app.run()