Compare commits

..

No commits in common. "120449386ab7fb0f3aec34bf37b3e21f3c088ba0" and "0ce3845a13697e6e7f3dae2781a64f5a063160c9" have entirely different histories.

6 changed files with 28 additions and 198 deletions

View File

@ -44,8 +44,6 @@ ADMIN_EMAIL=
THEME=default
VIEW_URI=/
"""
def __init__(self):
@ -88,16 +86,12 @@ VIEW_URI=/
if self.config.IN_MEMORY_DB:
self.db = GrungDB.with_schema(schema, storage=MemoryStorage)
else:
self.db = GrungDB.with_schema(
schema, self.path.database, sort_keys=True, indent=4, separators=(",", ": ")
)
self.db = GrungDB.with_schema(schema, self.path.database)
self.theme = Path(__file__).parent / "themes" / "default"
self.theme = Path(__file__).parent / 'themes' / "default"
self.web = Flask(self.config.NAME, template_folder=self.theme)
self.web.config["SECRET_KEY"] = self.config.SECRET_KEY
self.web.config["SEND_FILE_MAX_AGE_DEFAULT"] = 0
self.web.config["DEBUG"] = True
self._initialized = True
@ -105,35 +99,15 @@ VIEW_URI=/
if not self._initialized:
raise ApplicationNotInitializedError("This action requires the application to be initialized.")
def add_page(self, parent: schema.Page, child: schema.Page):
parent.pages.append(self.db.save(child))
parent = self.db.save(parent)
return parent.get_child(child)
def bootstrap(self):
"""
Bootstrap the database entries by populating the first Page, the Admin user and the Admins group.
"""
self.check_state()
home = schema.Page(stub=self.config.VIEW_URI, title="Home", body="This is the home page")
npcs = schema.Page(stub="NPC", title="NPC", body="NPCs!")
sabetha = schema.Page(title="Sabetha", body="Sabetha!")
try:
home = self.db.save(home)
self.db.save(schema.Page(parent_id=None, stub="", title="_", body=""))
except UniqueConstraintError:
pass
try:
npcs = self.add_page(home, npcs)
except UniqueConstraintError:
pass
try:
sabetha = self.add_page(npcs, sabetha)
except UniqueConstraintError:
pass
try:
admin = self.db.save(schema.User(name=self.config.ADMIN_USERNAME, email=self.config.ADMIN_EMAIL))
except UniqueConstraintError:

View File

@ -53,7 +53,6 @@ def init(context: typer.Context, drop: bool = typer.Option(False, help="Drop tab
ttfrog.app.db.close()
ttfrog.app.initialize(force=True)
ttfrog.app.bootstrap()
print(ttfrog.app.db.Page.all())
print(ttfrog.app.db)
@ -63,7 +62,6 @@ def run(context: typer.Context):
The default CLI entrypoint is ttfrog.cli.run().
"""
import ttfrog.web
ttfrog.app.web.run()

View File

@ -1,52 +1,18 @@
from grung.types import BackReference, Collection, Field, Record
from grung.types import Collection, Field, Record
class User(Record):
@classmethod
def fields(cls):
return [*super().fields(), Field("name"), Field("email", unique=True)]
_fields = [Field("name"), Field("email", unique=True)]
class Group(Record):
@classmethod
def fields(cls):
return [*super().fields(), Field("name", unique=True), Collection("users", User)]
_fields = [Field("name", unique=True), Collection("users", User)]
class Page(Record):
@classmethod
def fields(cls):
return [
*super().fields(),
Field("uri", unique=True),
_fields = [
Field("parent_id"),
Field("stub"),
Field("title"),
Field("body"),
Collection("pages", Page),
BackReference("parent", value_type=Page),
]
def before_insert(self, db):
super().before_insert(db)
if not self.stub and not self.title:
raise Exception("Must provide either a stub or a title!")
if not self.stub:
self.stub = self.title.title().replace(" ", "")
if not self.title:
self.title = self.stub
self.uri = (self.parent.uri + "/" if self.parent and self.parent.uri != "/" else "") + self.stub
def after_insert(self, db):
super().after_insert(db)
for child in self.pages:
obj = BackReference.dereference(child, db)
obj.uri = f"{self.uri}/{obj.stub}"
child = db.save(obj)
def get_child(self, obj: Record):
for page in self.pages:
if page.uid == obj.uid:
return page
return None

View File

@ -12,14 +12,8 @@
</head>
<body>
<nav>
{% for uri, stub in breadcrumbs %}
<span>&nbsp; / <a href="{{ app.config.VIEW_URI }}{{ uri }}">{{ stub }}</a></span>
{% endfor %}
</nav>
<nav>
Menu:
<ul>
{% block menu %}{% endblock %}
<li><a href="{{ url_for('index') }}">Home</a></li>
</ul>
</nav>

View File

@ -1,26 +1,6 @@
{% extends "base.html" %}
{% block menu %}
{% for child in page.pages %}
<li><a href="{{ app.config.VIEW_URI }}{{ child.uri }}">{{ child.title }}</a></li>
{% endfor %}
{% endblock %}
{% block content %}
<form method='POST'>
<input type="hidden" name="uid" value="{{ page.uid }}">
<h1>{{ page.doc_id }}: <input name='title' type='text' value="{{ page.title }}"></h1>
<h3>{{ app.config.VIEW_URI }}{{ page.parent.uri if page.parent else "/" }} <input name='stub' type='text' value='{{ page.stub }}'></h3>
<textarea name='body'>{{ page.body }}</textarea>
<input type=submit>
</form>
<pre>
{{ page }}
</pre>
<pre>
{{ app.web.config }}
</pre>
<h1>{{ page.title }}</h1>
{{ page.content }}
{% endblock %}

View File

@ -1,104 +1,22 @@
from flask import Response, render_template, request
from ttfrog import app
from flask import Response, render_template
from tinydb import where
from ttfrog import app, schema
from ttfrog.forms import PageForm
STATIC = ["static"]
import logging
def relative_uri(path: str = ""):
"""
The request's URI relative to the VIEW_URI without the leading '/'.
"""
return (path or request.path).replace(app.config.VIEW_URI, "", 1).strip("/") or "/"
def get_parent(uri: str):
try:
parent_uri = uri.strip("/").split("/", -1)[0]
except IndexError:
return None
app.web.logger.debug(f"Looking for parent with {parent_uri = }")
return get_page(parent_uri or "/", create_okay=False)
def get_page(path: str = "", create_okay: bool = False):
"""
Get one page, including its subpages, but not recursively.
"""
uri = path or relative_uri(request.path)
matches = app.db.Page.search(where("uri") == uri, recurse=False)
app.web.logger.debug(f"Found {len(matches)} pages where {uri = }")
if not matches:
if not create_okay:
return None
return schema.Page(stub=uri.split("/")[-1], body="This page does not exist", parent=get_parent(uri))
uids = [pointer.split("::")[-1] for pointer in matches[0].pages]
subpages = app.db.Page.search(where("uid").one_of(uids), recurse=False)
matches[0].pages = subpages
return matches[0]
def rendered(page: schema.Record, template: str = "page.html"):
if not page:
return Response("Page not found", status=404)
return render_template(template, page=page, app=app, breadcrumbs=breadcrumbs())
def get_static(path):
return Response("OK", status=200)
def breadcrumbs():
"""
Return (uri, stub) pairs for the parents leading from the VIEW_URI to the current request.
"""
if app.config.VIEW_URI != "/":
root = get_page()
yield (app.config.VIEW_URI, root.stub)
uri = ""
for stub in relative_uri().split("/"):
uri = "/".join([uri, stub]).lstrip("/")
yield (uri, stub)
logger = logging.getLogger(__name__)
@app.web.route("/")
def index():
return rendered(get_page(create_okay=False))
page = app.db.Page.search(where('stub') == "")
return render_template("page.html", page=page[0])
@app.web.route(f"{app.config.VIEW_URI}/<path:path>", methods=["GET"])
def view(path):
return rendered(get_page(request.path, create_okay=True))
@app.web.route(f"{app.config.VIEW_URI}/<path:path>", methods=["POST"])
def edit(path):
uri = relative_uri()
parent = get_parent(uri)
app.web.logger.debug(f"Handling form submission: {uri = }, {parent = }, {request.form = }")
if not parent:
return Response(f"Parent for {uri} does not exist.", status=403)
page = get_page(uri, create_okay=True)
app.web.logger.debug(f"Editing {page.doc_id} for {uri = }")
if page.doc_id:
if page.uid != request.form["uid"]:
return Response("Invalid UID.", status=403)
form = PageForm(page, request.form)
page = app.db.save(form.prepare())
app.web.logger.debug(f"Saved {page.doc_id}; now updating parent {parent.doc_id}")
parent.pages.append(page)
app.db.save(parent)
return get_page(stub)
@app.web.after_request
def add_header(r):
r.headers["Cache-Control"] = "no-cache, no-store, must-revalidate, public, max-age=0"
r.headers["Pragma"] = "no-cache"
r.headers["Expires"] = "0"
return r
@app.web.route("/<stub>")
def page_view(stub):
page = app.db.Page.search(where('stub') == stub)
if not page:
logger.info(f"No page found for {stub = }")
return Response(f"{stub}: not found", status=404)
return render_template("page.html", page=page[0])