Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion jottit/static/js/new-page.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
if (!value) return;
e.preventDefault();
const base = form.getAttribute("action") || "/";
const href = `${base}${encodeURIComponent(value)}?m=edit`;
const slug = value.toLowerCase().replaceAll(" ", "_");
const href = `${base}${encodeURIComponent(slug)}?m=edit`;
window.location.assign(href);
});
})();
10 changes: 10 additions & 0 deletions jottit/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ def page_slug(name: str) -> str:
return quote(name.lower().replace(" ", "_"))


def page_name_from_slug(slug: str) -> str:
"""Best-effort page name from a URL slug.

Page URLs are the lowercased page name with spaces written as
underscores. Flask has already percent-decoded the route segment by the
time views receive it.
"""
return slug.lower().replace("_", " ")


def site_root() -> str:
"""URL path prefix that page names hang off for the current request.

Expand Down
4 changes: 3 additions & 1 deletion jottit/views/page.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
)
from jottit.diff import better_diff
from jottit.render import format_content
from jottit.urls import page_slug, site_root
from jottit.urls import page_name_from_slug, page_slug, site_root


def home(site_slug: str) -> ResponseReturnValue:
Expand Down Expand Up @@ -70,6 +70,8 @@ def view(site_slug: str, page_name: str) -> ResponseReturnValue:
if mode == "edit" and not page_name and (new_name := request.args.get("name", "").strip()):
return redirect(f"{site_root()}{page_slug(new_name)}?m=edit", code=303)

page_name = page_name_from_slug(page_name)

action = _action_for(mode)
if (response := auth.gate(action)) is not None:
return response
Expand Down
23 changes: 23 additions & 0 deletions tests/test_page_edit.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,29 @@ def test_post_create_new_page_on_save(client: FlaskClient, db_engine: Engine) ->
assert rev.revision == 1


def test_post_create_page_with_space_redirects_to_readable_slug(
client: FlaskClient, db_engine: Engine
) -> None:
site_id = _seed_site(db_engine, secret_url="e7space", public_url="etaspace")

response = client.post(
"/foo_bar",
base_url="http://etaspace.jottit.test/",
data={"content": "first version"},
)

assert response.status_code == 303
assert response.headers["Location"].endswith("/foo_bar")

follow = client.get("/foo_bar", base_url="http://etaspace.jottit.test/")
assert follow.status_code == 200
assert b"first version" in follow.data

with db_engine.connect() as conn:
page = get_page(conn, site_id=site_id, page_name="foo bar")
assert page is not None


def test_post_create_via_secret_url_redirects_under_prefix(
client: FlaskClient, db_engine: Engine
) -> None:
Expand Down
8 changes: 2 additions & 6 deletions tests/test_page_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,7 @@ def test_wikilinks_resolve_against_subdomain_root(client: FlaskClient, db_engine
# ---- home_layout=feed ----


def test_home_renders_feed_when_home_layout_is_feed(
client: FlaskClient, db_engine: Engine
) -> None:
def test_home_renders_feed_when_home_layout_is_feed(client: FlaskClient, db_engine: Engine) -> None:
site_id = _seed_site(db_engine, secret_url="fd1", public_url="feed-alpha", content="home body")
with db_engine.begin() as conn:
new_page(conn, site_id=site_id, name="post-one", content="**first post**")
Expand Down Expand Up @@ -197,9 +195,7 @@ def test_home_feed_empty_state(client: FlaskClient, db_engine: Engine) -> None:
assert "No pages yet" in response.data.decode()


def test_home_still_renders_page_in_default_mode(
client: FlaskClient, db_engine: Engine
) -> None:
def test_home_still_renders_page_in_default_mode(client: FlaskClient, db_engine: Engine) -> None:
# Sanity-check the default branch survives the home_layout addition.
_seed_site(db_engine, secret_url="fd3", public_url="feed-gamma", content="just a page")

Expand Down
9 changes: 8 additions & 1 deletion tests/test_urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from flask import Flask

from jottit.urls import page_slug, site_root
from jottit.urls import page_name_from_slug, page_slug, site_root

# ---- page_slug ----

Expand All @@ -19,6 +19,13 @@ def test_page_slug_empty_returns_empty() -> None:
assert page_slug("") == ""


# ---- page_name_from_slug ----


def test_page_name_from_slug_restores_spaces_and_lowercases() -> None:
assert page_name_from_slug("Foo_Bar") == "foo bar"


# ---- site_root ----


Expand Down
Loading