vtt/src/ttfrog/web.py

165 lines
5.3 KiB
Python
Raw Normal View History

2025-10-05 00:15:37 -07:00
from flask import Response, g, redirect, render_template, request, session, url_for
2025-09-27 16:20:08 -07:00
from tinydb import where
2025-09-28 14:14:16 -07:00
2025-10-05 00:15:37 -07:00
from ttfrog import app, forms, schema
2025-09-21 22:11:56 -07:00
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 "/"
2025-10-04 01:26:09 -07:00
def get_parent(table: str, uri: str):
try:
parent_uri = uri.strip("/").rsplit("/", 1)[0]
except IndexError:
return None
2025-10-05 00:15:37 -07:00
return get_page(parent_uri, table=table if "/" in parent_uri else "Page", create_okay=False)
2025-10-04 01:26:09 -07:00
2025-10-12 15:36:38 -07:00
def get_page(path: str, table: str = "Page", create_okay: bool = False):
2025-09-28 14:14:16 -07:00
"""
2025-10-04 01:26:09 -07:00
Get one page, including its members, but not recursively.
2025-09-28 14:14:16 -07:00
"""
2025-10-12 15:36:38 -07:00
uri = relative_uri(path)
2025-10-04 01:26:09 -07:00
if table not in app.db.tables():
2025-10-18 11:23:03 -07:00
app.web.logger.debug(f"Table {table} does not exist in {app.db.tables()}.")
2025-10-04 01:26:09 -07:00
return None
2025-10-05 00:15:37 -07:00
page = app.db.table(table).get(where("uri") == uri, recurse=False)
if not page:
2025-10-18 11:23:03 -07:00
app.web.logger.debug("Page does not exist.")
if not create_okay:
2025-10-18 11:23:03 -07:00
app.web.logger.debug("Page does not exist and creating is not okay.")
return None
2025-10-05 00:15:37 -07:00
parent = get_parent(table, uri)
2025-10-18 11:23:03 -07:00
if not app.authorize(g.user, parent, schema.Permissions.WRITE):
app.web.logger.debug(f"User {g.user} is not authorized to write {parent}")
2025-10-05 00:15:37 -07:00
return None
return getattr(schema, table)(name=uri.split("/")[-1], body="This page does not exist", parent=parent)
2025-10-07 01:18:36 -07:00
if not app.authorize(g.user, page, schema.Permissions.READ):
2025-10-05 00:15:37 -07:00
return None
if hasattr(page, "members"):
2025-10-04 01:26:09 -07:00
subpages = []
2025-10-05 00:15:37 -07:00
for pointer in page.members:
2025-10-08 00:46:09 -07:00
table, pkey, pval = pointer.split("::")
subpages += app.db.table(table).search(where(pkey) == pval, recurse=False)
2025-10-04 01:26:09 -07:00
page.members = subpages
return page
2025-09-21 22:11:56 -07:00
2025-09-27 16:20:08 -07:00
def rendered(page: schema.Record, template: str = "page.html"):
if not page:
return Response("Page not found", status=404)
2025-10-12 15:36:38 -07:00
return render_template(
template,
page=page,
app=app,
breadcrumbs=breadcrumbs(),
root=g.root,
user=g.user,
g=g
)
def breadcrumbs():
"""
2025-10-04 01:26:09 -07:00
Return (uri, name) pairs for the parents leading from the VIEW_URI to the current request.
"""
uri = ""
2025-10-04 01:26:09 -07:00
for name in relative_uri().split("/"):
2025-10-12 15:36:38 -07:00
uri = "/".join([uri, name])
2025-10-04 01:26:09 -07:00
yield (uri, name)
2025-10-12 15:36:38 -07:00
@app.web.route(app.config.VIEW_URI)
2025-09-27 16:20:08 -07:00
def index():
2025-10-12 15:36:38 -07:00
return rendered(get_page(app.config.VIEW_URI, create_okay=False))
2025-09-27 16:20:08 -07:00
2025-10-05 00:15:37 -07:00
@app.web.route("/login", methods=["GET", "POST"])
2025-10-04 10:48:18 -07:00
def login():
app.web.session_interface.regenerate(session)
2025-10-05 00:15:37 -07:00
if request.method == "POST":
username = request.form.get("username")
password = request.form.get("password")
2025-10-07 01:18:36 -07:00
user = app.authenticate(username, password)
if user:
g.user = user
session["user_id"] = g.user.doc_id
session["user"] = dict(g.user.serialize())
2025-10-05 00:15:37 -07:00
return redirect(url_for("index"))
g.messages.append(f"Invalid login for {username}")
return rendered(schema.Page(name="Login", title="Please enter your login details"), "login.html")
2025-10-04 10:48:18 -07:00
@app.web.route("/logout")
def logout():
2025-10-05 00:15:37 -07:00
if "user_id" in session:
del session["user_id"]
2025-10-04 10:48:18 -07:00
del g.user
2025-10-08 00:46:09 -07:00
return redirect(url_for("index"))
2025-10-04 10:48:18 -07:00
2025-10-04 01:26:09 -07:00
@app.web.route(f"{app.config.VIEW_URI}/<path:table>/<path:path>", methods=["GET"])
2025-10-05 00:15:37 -07:00
@app.web.route(f"{app.config.VIEW_URI}/<path:path>", methods=["GET"], defaults={"table": "Page"})
2025-10-04 01:26:09 -07:00
def view(table, path):
parent = get_parent(table, relative_uri())
2025-10-18 11:23:03 -07:00
if table not in app.db.tables():
table = parent.__class__.__name__ if parent else "Page"
2025-10-12 15:36:38 -07:00
page = get_page(request.path, table=table, create_okay=(parent and parent.doc_id is not None))
return rendered(page)
2025-10-04 01:26:09 -07:00
@app.web.route(f"{app.config.VIEW_URI}/<path:table>/<path:path>", methods=["POST"])
2025-10-05 00:15:37 -07:00
@app.web.route(f"{app.config.VIEW_URI}/<path:path>", methods=["POST"], defaults={"table": "Page"})
2025-10-04 01:26:09 -07:00
def edit(table, path):
uri = relative_uri()
2025-10-04 01:26:09 -07:00
parent = get_parent(table, uri)
if not parent:
return Response("You cannot create a page at this location.", status=403)
2025-10-04 01:26:09 -07:00
# get or create the docoument at this uri
page = get_page(uri, table=table, create_okay=True)
2025-10-07 01:18:36 -07:00
if not app.authorize(g.user, page, schema.Permissions.WRITE):
return Response("Permission denied.", status=403)
2025-10-04 01:26:09 -07:00
save_data = getattr(forms, table)(page, request.form).prepare()
# editing existing document
if page.doc_id:
if page.uid != request.form["uid"]:
return Response("Invalid UID.", status=403)
2025-10-04 01:26:09 -07:00
return rendered(app.db.save(save_data))
2025-10-04 01:26:09 -07:00
# saving a new document
2025-10-07 01:18:36 -07:00
return rendered(parent.add_member(save_data))
2025-09-28 14:14:16 -07:00
2025-10-04 10:48:18 -07:00
@app.web.before_request
def before_request():
2025-10-05 00:15:37 -07:00
g.messages = []
2025-10-17 19:33:29 -07:00
if not request.path.startswith('/static'):
user_id = session.get("user_id", 1)
g.user = app.db.User.get(doc_id=user_id)
session["user_id"] = user_id
session["user"] = dict(g.user.serialize())
g.root = get_page(app.config.VIEW_URI)
2025-10-04 10:48:18 -07:00
2025-09-28 14:14:16 -07:00
@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