from flask import Response, render_template, request from tinydb import where from ttfrog import app, schema from ttfrog.forms import PageForm STATIC = ["static"] 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) @app.web.route("/") def index(): return rendered(get_page(create_okay=False)) @app.web.route(f"{app.config.VIEW_URI}/", methods=["GET"]) def view(path): return rendered(get_page(request.path, create_okay=True)) @app.web.route(f"{app.config.VIEW_URI}/", 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