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 THEME=default
VIEW_URI=/
""" """
def __init__(self): def __init__(self):
@ -88,16 +86,12 @@ VIEW_URI=/
if self.config.IN_MEMORY_DB: if self.config.IN_MEMORY_DB:
self.db = GrungDB.with_schema(schema, storage=MemoryStorage) self.db = GrungDB.with_schema(schema, storage=MemoryStorage)
else: else:
self.db = GrungDB.with_schema( self.db = GrungDB.with_schema(schema, self.path.database)
schema, self.path.database, sort_keys=True, indent=4, separators=(",", ": ")
)
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 = Flask(self.config.NAME, template_folder=self.theme)
self.web.config["SECRET_KEY"] = self.config.SECRET_KEY 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 self._initialized = True
@ -105,35 +99,15 @@ VIEW_URI=/
if not self._initialized: if not self._initialized:
raise ApplicationNotInitializedError("This action requires the application to be 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): def bootstrap(self):
""" """
Bootstrap the database entries by populating the first Page, the Admin user and the Admins group. Bootstrap the database entries by populating the first Page, the Admin user and the Admins group.
""" """
self.check_state() 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: try:
home = self.db.save(home) self.db.save(schema.Page(parent_id=None, stub="", title="_", body=""))
except UniqueConstraintError: except UniqueConstraintError:
pass pass
try:
npcs = self.add_page(home, npcs)
except UniqueConstraintError:
pass
try:
sabetha = self.add_page(npcs, sabetha)
except UniqueConstraintError:
pass
try: try:
admin = self.db.save(schema.User(name=self.config.ADMIN_USERNAME, email=self.config.ADMIN_EMAIL)) admin = self.db.save(schema.User(name=self.config.ADMIN_USERNAME, email=self.config.ADMIN_EMAIL))
except UniqueConstraintError: 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.db.close()
ttfrog.app.initialize(force=True) ttfrog.app.initialize(force=True)
ttfrog.app.bootstrap() ttfrog.app.bootstrap()
print(ttfrog.app.db.Page.all())
print(ttfrog.app.db) print(ttfrog.app.db)
@ -63,7 +62,6 @@ def run(context: typer.Context):
The default CLI entrypoint is ttfrog.cli.run(). The default CLI entrypoint is ttfrog.cli.run().
""" """
import ttfrog.web import ttfrog.web
ttfrog.app.web.run() 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): class User(Record):
@classmethod _fields = [Field("name"), Field("email", unique=True)]
def fields(cls):
return [*super().fields(), Field("name"), Field("email", unique=True)]
class Group(Record): class Group(Record):
@classmethod _fields = [Field("name", unique=True), Collection("users", User)]
def fields(cls):
return [*super().fields(), Field("name", unique=True), Collection("users", User)]
class Page(Record): class Page(Record):
@classmethod _fields = [
def fields(cls): Field("parent_id"),
return [
*super().fields(),
Field("uri", unique=True),
Field("stub"), Field("stub"),
Field("title"), Field("title"),
Field("body"), 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> </head>
<body> <body>
<nav> <nav>
{% for uri, stub in breadcrumbs %}
<span>&nbsp; / <a href="{{ app.config.VIEW_URI }}{{ uri }}">{{ stub }}</a></span>
{% endfor %}
</nav>
<nav>
Menu:
<ul> <ul>
{% block menu %}{% endblock %} <li><a href="{{ url_for('index') }}">Home</a></li>
</ul> </ul>
</nav> </nav>

View File

@ -1,26 +1,6 @@
{% extends "base.html" %} {% 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 %} {% block content %}
<form method='POST'> <h1>{{ page.title }}</h1>
<input type="hidden" name="uid" value="{{ page.uid }}"> {{ page.content }}
<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>
{% endblock %} {% 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 tinydb import where
import logging
from ttfrog import app, schema
from ttfrog.forms import PageForm
STATIC = ["static"]
def relative_uri(path: str = ""): logger = logging.getLogger(__name__)
"""
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)
@app.web.route("/") @app.web.route("/")
def index(): 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"]) @app.web.route("/<stub>")
def view(path): def page_view(stub):
return rendered(get_page(request.path, create_okay=True)) page = app.db.Page.search(where('stub') == stub)
if not page:
logger.info(f"No page found for {stub = }")
@app.web.route(f"{app.config.VIEW_URI}/<path:path>", methods=["POST"]) return Response(f"{stub}: not found", status=404)
def edit(path): return render_template("page.html", page=page[0])
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