diff --git a/README.md b/README.md
index 0a6f2a2..705142a 100644
--- a/README.md
+++ b/README.md
@@ -27,6 +27,8 @@ data/
«Historikk»-seksjon i editoren med diff per endring; hele loggen ligger under Innstillinger
(eller `git -C data/content log`).
* **Gjester** ser og redigerer bare egne innlegg. Hovedbrukeren ser alt og administrerer gjester.
+* **Signatur**: innlegg avsluttes med «– Visningsnavn», evt. med e-post som mailto-lenke. Begge
+ settes under Profil i admin og slås opp ved bygging, så en endring gjelder alle innlegg.
## Kjøre lokalt
diff --git a/soapbox/admin.py b/soapbox/admin.py
index 5fa156e..760cf67 100644
--- a/soapbox/admin.py
+++ b/soapbox/admin.py
@@ -61,6 +61,7 @@ bp = Blueprint(
)
USERNAME_RE = re.compile(r"^[a-z0-9][a-z0-9-]{1,30}$")
+EMAIL_RE = re.compile(r"^[^@\s]+@[^@\s]+\.[^@\s]+$")
# Reserverte navn som ville kollidert med URL-strukturen
RESERVED = {"admin", "theme", "feed.xml", "posts", "guests", "index.html"}
@@ -244,7 +245,7 @@ def new():
body="",
date=_now(),
draft=True,
- author=g.user.display_name,
+ author=g.user.username,
guest=guest,
)
save_post(cfg, post)
@@ -407,13 +408,16 @@ def users():
flash("Passordet må være minst 8 tegn.", "error")
else:
try:
- db.create_user(
+ user = db.create_user(
g.db,
username,
password,
db.ROLE_GUEST,
request.form.get("display_name", ""),
)
+ email = request.form.get("email", "").strip()
+ if email and EMAIL_RE.match(email):
+ db.update_profile(g.db, user.id, user.display_name, email)
flash(f"Gjestebruker {username} opprettet.", "ok")
except sqlite3.IntegrityError:
flash("Brukernavnet er allerede i bruk.", "error")
@@ -448,6 +452,22 @@ def password():
return render_template("admin/password.html")
+@bp.route("/profile", methods=["GET", "POST"])
+@login_required
+def profile():
+ """Visningsnavn (signatur på innlegg) og e-post (vises offentlig hvis satt)."""
+ if request.method == "POST":
+ email = request.form.get("email", "").strip()
+ if email and not EMAIL_RE.match(email):
+ flash("Ugyldig e-postadresse.", "error")
+ else:
+ db.update_profile(g.db, g.user.id, request.form.get("display_name", ""), email)
+ build_site(_cfg()) # signaturen ligger i de genererte sidene
+ flash("Profilen er lagret.", "ok")
+ return redirect(url_for("admin.profile"))
+ return render_template("admin/profile.html", me=db.get_user(g.db, g.user.id))
+
+
@bp.route("/settings", methods=["GET", "POST"])
@owner_required
def settings():
diff --git a/soapbox/build.py b/soapbox/build.py
index 36b833c..be1d4cc 100644
--- a/soapbox/build.py
+++ b/soapbox/build.py
@@ -23,6 +23,7 @@ from xml.sax.saxutils import escape
from jinja2 import ChoiceLoader, Environment, FileSystemLoader, select_autoescape
+from . import db
from .config import Config, load_site_settings
from .content import IMAGE_EXT, Post, list_posts
from .render import excerpt, relative_to, render_markdown
@@ -50,6 +51,7 @@ def build_site(cfg: Config) -> int:
site = load_site_settings(cfg)
env = _env(cfg)
posts = list_posts(cfg)
+ _resolve_authors(cfg, posts)
out = cfg.public_dir
# Bygg i en midlertidig katalog og bytt atomisk, så besøkende aldri ser
@@ -90,7 +92,7 @@ def build_site(cfg: Config) -> int:
for guest, gposts in guests.items():
_write(
tmp / guest / "index.html",
- render("index.html", f"{guest}/", posts=gposts, heading=gposts[0].author or guest),
+ render("index.html", f"{guest}/", posts=gposts, heading=gposts[0].author_name or guest),
)
_write(tmp / "feed.xml", _feed(cfg, site, posts[:20]))
@@ -113,6 +115,28 @@ def build_site(cfg: Config) -> int:
return len(posts)
+def _resolve_authors(cfg: Config, posts: list[Post]) -> None:
+ """Fyller author_name/author_email fra brukertabellen.
+
+ Frontmatter lagrer brukernavnet (stabilt); visningsnavn og e-post kan
+ endres i profilen og slår da gjennom på alle innlegg ved neste bygg.
+ Finnes ikke brukeren (f.eks. innhold klonet uten brukerdatabase), vises
+ verdien fra frontmatter som den er.
+ """
+ conn = db.connect(cfg.db_path)
+ try:
+ users = db.authors(conn)
+ finally:
+ conn.close()
+ for p in posts:
+ key = p.author or (p.guest or "")
+ user = users.get(key)
+ if user is None and p.guest is None:
+ user = next((u for u in users.values() if u.is_owner), None)
+ p.author_name = user.display_name if user else (p.author or p.guest or "")
+ p.author_email = user.email if user else ""
+
+
def _sitemap(cfg: Config, posts: list[Post], guests: list[str]) -> str:
"""sitemap.xml med forsiden, alle innlegg og gjesteoversiktene."""
base = cfg.site_url + "/"
@@ -145,7 +169,7 @@ def _feed(cfg: Config, site: dict, posts: list[Post]) -> str:
f"{escape(link)}"
f"
{{ u.username }}
+ {{ u.display_name }} {{ u.username }}{% if u.email %} {% endif %}
{% if u.is_owner %}hovedbruker{% else %}{% endif %}
{% if not u.is_owner %}
{{ text }}
{% endif %} diff --git a/soapbox/themes/green/templates/post.html b/soapbox/themes/green/templates/post.html index 443cff5..bb56802 100644 --- a/soapbox/themes/green/templates/post.html +++ b/soapbox/themes/green/templates/post.html @@ -12,7 +12,7 @@ {% if post.images %}{% endif %} {% if post.updated %}{% endif %} -{% if post.author %}{% endif %} +{% if post.author_name %}{% endif %} {% endblock %} {% block content %} @@ -21,10 +21,14 @@– {{ post.author_name }}{% if post.author_email %} + · {{ post.author_email }}{% endif %}
+ {% endif %} {% endblock %} diff --git a/tests/test_app.py b/tests/test_app.py index e607685..d027dc5 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -314,3 +314,28 @@ def test_base_path_derived_from_site_url(tmp_path): } ) assert cfg.base_path == "" + + +def test_signature_from_profile(client, cfg): + post_id = _create_post(client) + token = csrf(client) + client.post( + f"/admin/edit/{post_id}", + data={"csrf": token, "title": "Sign", "body": "x", "action": "publish"}, + ) + assert "– Ole" in client.get("/sign/").text # visningsnavn fra brukertabellen + assert (cfg.content_dir / "posts/sign/index.md").read_text().count('author: "eier"') == 1 + + r = client.post( + "/admin/profile", data={"csrf": token, "display_name": "Ole M.", "email": "ole@example.no"} + ) + assert r.status_code == 302 + html = client.get("/sign/").text + assert "– Ole M." in html and 'href="mailto:ole@example.no"' in html + assert "