fix(templates): persist workspace context so template pages actually save
Pages created via the page-template picker (weekly report, project doc, meeting notes, ...) previously got workspace_id=NULL and workspace=login, so they never appeared in the active local/Gitea workspace tree — they looked like they 'didn't save' even though the row existed. Frontend: _useTemplate now sends the active workspace context (workspace_id or workspace key), mirroring _createPlainPage. Backend: use_page_template resolves/validates the workspace and persists both workspace and workspace_id on the new page.
This commit is contained in:
+37
-7
@@ -248,20 +248,50 @@ async def use_page_template(request: Request, template_id: int):
|
||||
blocks_json = row["blocks_json"]
|
||||
name = row["name"]
|
||||
title = title or row["name"]
|
||||
# Resolve the target workspace so the new page actually shows up in the
|
||||
# active local workspace (bugfix: template pages previously got
|
||||
# workspace_id = NULL and never appeared in the sidebar/tree).
|
||||
uid = (user or {}).get("id")
|
||||
ws_id_raw = body.get("workspace_id")
|
||||
ws_id = None
|
||||
if ws_id_raw is not None:
|
||||
try:
|
||||
ws_id = int(ws_id_raw)
|
||||
except (TypeError, ValueError):
|
||||
ws_id = None
|
||||
ws_key = user.get("login", "Bruno") if user else "Bruno"
|
||||
if ws_id is not None:
|
||||
with get_conn() as conn:
|
||||
ws_row = conn.execute(
|
||||
"SELECT id, name, owner_id FROM workspaces WHERE id=?", (ws_id,)
|
||||
).fetchone()
|
||||
if ws_row and (uid is None or ws_row["owner_id"] == uid):
|
||||
ws_key = ws_row["name"] or ws_key
|
||||
else:
|
||||
ws_id = None
|
||||
else:
|
||||
body_ws = (body.get("workspace") or "").strip()
|
||||
if body_ws:
|
||||
ws_key = body_ws
|
||||
try:
|
||||
json.loads(blocks_json)
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
raise HTTPException(500, "Template content corrupted") from None
|
||||
with get_conn() as conn:
|
||||
next_order = conn.execute(
|
||||
"SELECT COALESCE(MAX(sort_order), -1) + 1 FROM pages WHERE workspace=? AND parent_id IS NULL",
|
||||
(ws_key,),
|
||||
).fetchone()[0]
|
||||
if ws_id is not None:
|
||||
next_order = conn.execute(
|
||||
"SELECT COALESCE(MAX(sort_order), -1) + 1 FROM pages WHERE workspace_id=? AND parent_id IS NULL",
|
||||
(ws_id,),
|
||||
).fetchone()[0]
|
||||
else:
|
||||
next_order = conn.execute(
|
||||
"SELECT COALESCE(MAX(sort_order), -1) + 1 FROM pages WHERE workspace=? AND parent_id IS NULL",
|
||||
(ws_key,),
|
||||
).fetchone()[0]
|
||||
cur = conn.execute(
|
||||
"""INSERT INTO pages (workspace, title, content, content_format, parent_section, sort_order)
|
||||
VALUES (?,?,?,?, 'Private', ?)""",
|
||||
(ws_key, title or name, blocks_json, "blocks", next_order),
|
||||
"""INSERT INTO pages (workspace, workspace_id, title, content, content_format, parent_section, sort_order)
|
||||
VALUES (?,?,?,?,?, 'Private', ?)""",
|
||||
(ws_key, ws_id, title or name, blocks_json, "blocks", next_order),
|
||||
)
|
||||
conn.commit()
|
||||
page_id = cur.lastrowid
|
||||
|
||||
@@ -981,7 +981,12 @@
|
||||
var parts = use.split(':');
|
||||
var body = {};
|
||||
var url;
|
||||
if (parts[0] === 'key') { url = '/board/api/page-templates/0/use'; body = {key: parts.slice(1).join(':')}; }
|
||||
// Pass the current workspace context so the created page is persisted
|
||||
// in the active local/Gitea workspace (mirrors _createPlainPage()).
|
||||
var ctx = self._getContext();
|
||||
if (ctx.wsId > 0) { body.workspace_id = ctx.wsId; }
|
||||
else if (ctx.workspaceKey) { body.workspace = ctx.workspaceKey; }
|
||||
if (parts[0] === 'key') { body.key = parts.slice(1).join(':'); url = '/board/api/page-templates/0/use'; }
|
||||
else { url = '/board/api/page-templates/' + parts[1] + '/use'; }
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
|
||||
@@ -283,6 +283,39 @@ def test_use_builtin_template_creates_page(client):
|
||||
cookies={"flowdeck_session": s}, headers={"X-CSRF-Token": c}).status_code == 404
|
||||
|
||||
|
||||
def test_use_template_persists_workspace_id(client):
|
||||
s, c, uid = _login(client)
|
||||
# Create a local workspace owned by this user
|
||||
r = client.post("/api/workspaces", json={"name": "Tpl WS"},
|
||||
cookies={"flowdeck_session": s}, headers={"X-CSRF-Token": c})
|
||||
assert r.status_code == 200
|
||||
ws_id = r.json()["id"]
|
||||
client.post(f"/api/workspaces/{ws_id}/select", cookies={"flowdeck_session": s})
|
||||
# built-in template use with workspace context
|
||||
r2 = client.post("/board/api/page-templates/0/use", json={"key": "meeting_notes", "workspace_id": ws_id},
|
||||
cookies={"flowdeck_session": s}, headers={"X-CSRF-Token": c})
|
||||
assert r2.status_code == 200
|
||||
pid = r2.json()["id"]
|
||||
page = client.get(f"/board/api/pages/{pid}").json()
|
||||
assert page["workspace_id"] == ws_id
|
||||
assert page["workspace"] == "Tpl WS"
|
||||
# user template use with workspace context
|
||||
r3 = client.post("/board/api/page-templates",
|
||||
json={"name": "Tpl WS template", "blocks": _blocks_b("ws body")},
|
||||
cookies={"flowdeck_session": s}, headers={"X-CSRF-Token": c})
|
||||
tid = r3.json()["id"]
|
||||
r4 = client.post(f"/board/api/page-templates/{tid}/use", json={"workspace_id": ws_id},
|
||||
cookies={"flowdeck_session": s}, headers={"X-CSRF-Token": c})
|
||||
assert r4.status_code == 200
|
||||
page2 = client.get(f"/board/api/pages/{r4.json()['id']}").json()
|
||||
assert page2["workspace_id"] == ws_id
|
||||
assert page2["workspace"] == "Tpl WS"
|
||||
# routing: template-created page must appear in the workspace tree
|
||||
tree = client.get("/api/local-workspace/tree", cookies={"flowdeck_session": s}).json()
|
||||
ids = [n["id"] for n in tree["tree"]]
|
||||
assert pid in ids
|
||||
|
||||
|
||||
def test_user_template_roundtrip(client):
|
||||
s, c, uid = _login(client)
|
||||
pid = _mk_page(client, s, "My Weekly", _blocks_b("body text"))
|
||||
|
||||
Reference in New Issue
Block a user