diff --git a/README.md b/README.md
index f7418fb6..0993321b 100644
--- a/README.md
+++ b/README.md
@@ -51,6 +51,19 @@ The `deploy.sh` script deploys
All files are generated by the according markdown `.md` file in the `www` directory.
+### Refining the web pages
+
+The `scripts/wwwdev.sh` script supports live development of the web presence:
+
+```
+ scripts/wwwdev.sh
+```
+
+this will continously regenerate all markdown files from
+the `www/build` from the `www/src` directory.
+Moreover, it will start a browser window automatically.
+
+
### Ports
Postfix listens on ports 25 (smtp) and 587 (submission) and 465 (submissions).
diff --git a/deploy-chatmail/src/deploy_chatmail/__init__.py b/deploy-chatmail/src/deploy_chatmail/__init__.py
index 197526bf..20dc0dfd 100644
--- a/deploy-chatmail/src/deploy_chatmail/__init__.py
+++ b/deploy-chatmail/src/deploy_chatmail/__init__.py
@@ -3,7 +3,6 @@ Chat Mail pyinfra deploy.
"""
import importlib.resources
import configparser
-import textwrap
from pathlib import Path
from pyinfra import host
@@ -11,11 +10,6 @@ from pyinfra.operations import apt, files, server, systemd
from pyinfra.facts.files import File
from pyinfra.facts.systemd import SystemdEnabled
from .acmetool import deploy_acmetool
-import markdown
-from jinja2 import Template
-
-
-from .genqr import gen_qr_png_data
def _install_chatmaild() -> None:
@@ -325,67 +319,6 @@ def get_ini_settings(mail_domain, inipath):
return settings
-def build_htmlj2_from_markdown(source):
- assert source.exists(), source
- template_content = open(source).read()
- if source.stem == "privacy":
- title = "privacy {{ config.mail_domain }}"
- elif source.stem == "index":
- title = "home {{ config.mail_domain }}"
- elif source.stem == "info":
- title = "info {{ config.mail_domain }}"
-
- html = markdown.markdown(template_content)
- html = (
- textwrap.dedent(
- f"""\
-
-
-
-
- {title}
-
-
-
- """
- )
- + html
- + "\n"
- + textwrap.dedent(
- """\
-
- """
- )
- )
-
- target_path = source.with_name(source.stem + ".html.j2")
- with open(target_path, "w") as f:
- f.write(html)
- print(f"wrote {target_path}")
- return target_path
-
-
-def build_webpages(www_path, config):
- mail_domain = config["mail_domain"]
- qr_data = gen_qr_png_data(mail_domain).read()
- www_path.joinpath(f"qr-chatmail-invite-{mail_domain}.png").write_bytes(qr_data)
-
- for path in www_path.iterdir():
- if path.suffix == ".md":
- path = build_htmlj2_from_markdown(path)
-
- if path.suffix == ".j2":
- target = path.with_name(path.name[:-3])
- template = Template(path.read_text())
- with target.open("w") as f:
- f.write(template.render(config=config))
-
-
def deploy_chatmail(mail_domain: str, mail_server: str, dkim_selector: str) -> None:
"""Deploy a chat-mail instance.
@@ -393,6 +326,7 @@ def deploy_chatmail(mail_domain: str, mail_server: str, dkim_selector: str) -> N
:param mail_server: the DNS name under which your mail server is reachable
:param dkim_selector:
"""
+ from .www import build_webpages
apt.update(name="apt update", cache_time=24 * 3600)
server.group(name="Create vmail group", group="vmail", system=True)
@@ -442,8 +376,10 @@ def deploy_chatmail(mail_domain: str, mail_server: str, dkim_selector: str) -> N
config = get_ini_settings(mail_domain, chatmail_ini)
www_path = pkg_root.joinpath("../../../www").resolve()
- build_webpages(www_path, config)
- files.rsync(f"{www_path}/", "/var/www/html", flags=["-avz"])
+ build_dir = www_path.joinpath("build")
+ src_dir = www_path.joinpath("src")
+ build_webpages(src_dir, build_dir, config)
+ files.rsync(f"{build_dir}/", "/var/www/html", flags=["-avz"])
_install_chatmaild()
debug = False
diff --git a/deploy-chatmail/src/deploy_chatmail/genqr.py b/deploy-chatmail/src/deploy_chatmail/genqr.py
index 8ae7fd24..21419859 100644
--- a/deploy-chatmail/src/deploy_chatmail/genqr.py
+++ b/deploy-chatmail/src/deploy_chatmail/genqr.py
@@ -49,10 +49,10 @@ def gen_qr(maildomain, url):
size = width = 384
qr_padding = 6
text_height = font_size * num_lines
- height = size + text_height + qr_padding * 2
+ height = size + text_height
image = Image.new("RGBA", (width, height), "white")
- qr_final_size = width - (qr_padding * 2)
+ qr_final_size = width
if num_lines:
draw = ImageDraw.Draw(image)
diff --git a/deploy-chatmail/src/deploy_chatmail/www.py b/deploy-chatmail/src/deploy_chatmail/www.py
new file mode 100644
index 00000000..57048f5c
--- /dev/null
+++ b/deploy-chatmail/src/deploy_chatmail/www.py
@@ -0,0 +1,94 @@
+import importlib.resources
+import webbrowser
+import hashlib
+import time
+
+import markdown
+from jinja2 import Template
+from .genqr import gen_qr_png_data
+from deploy_chatmail import get_ini_settings
+
+
+def snapshot_dir_stats(somedir):
+ d = {}
+ for path in somedir.iterdir():
+ if path.is_file() and path.name[0] != "." and path.suffix != ".swp":
+ mtime = path.stat().st_mtime
+ hash = hashlib.md5(path.read_bytes()).hexdigest()
+ d[path] = (mtime, hash)
+ return d
+
+
+def prepare_template(source):
+ assert source.exists(), source
+ render_vars = {}
+ render_vars["pagename"] = "home" if source.stem == "index" else source.stem
+ render_vars["markdown_html"] = markdown.markdown(source.read_text())
+ page_layout = source.with_name("page-layout.html").read_text()
+ return render_vars, page_layout
+
+
+def build_webpages(src_dir, build_dir, config):
+ mail_domain = config["mail_domain"]
+ assert src_dir.exists(), src_dir
+ if not build_dir.exists():
+ build_dir.mkdir()
+
+ qr_path = build_dir.joinpath(f"qr-chatmail-invite-{mail_domain}.png")
+ qr_path.write_bytes(gen_qr_png_data(mail_domain).read())
+
+ for path in src_dir.iterdir():
+ if path.suffix == ".md":
+ render_vars, content = prepare_template(path)
+ target = build_dir.joinpath(path.stem + ".html")
+
+ # recursive jinja2 rendering
+ while 1:
+ new = Template(content).render(config=config, **render_vars)
+ if new == content:
+ break
+ content = new
+
+ with target.open("w") as f:
+ f.write(content)
+ elif path.name != "page-layout.html":
+ target = build_dir.joinpath(path.name)
+ target.write_bytes(path.read_bytes())
+ return build_dir
+
+
+def main():
+ path = importlib.resources.files(__package__)
+ reporoot = path.joinpath("../../../").resolve()
+ inipath = reporoot.joinpath("chatmail.ini")
+ config = get_ini_settings("example.testrun.org", inipath)
+ www_path = reporoot.joinpath("www")
+ src_path = www_path.joinpath("src")
+ stats = snapshot_dir_stats(src_path)
+ build_dir = www_path.joinpath("build")
+ src_dir = www_path.joinpath("src")
+ build_webpages(src_dir, build_dir, config)
+ index_path = build_dir.joinpath("index.html")
+ webbrowser.open(str(index_path))
+ print(f"started webbrowser-window for f{index_path}")
+
+ print(f"watching {src_path} directory for changes")
+ count = 0
+ while 1:
+ newstats = snapshot_dir_stats(src_path)
+ if newstats == stats and count < 60:
+ count += 1
+ time.sleep(1.0)
+ continue
+ for key in newstats:
+ if stats[key] != newstats[key]:
+ print(f"*** CHANGED: {key}")
+
+ stats = newstats
+ build_webpages(src_dir, build_dir, config)
+ print(f"regenerated web pages at: {index_path}")
+ count = 0
+
+
+if __name__ == "__main__":
+ main()
diff --git a/scripts/wwwdev.sh b/scripts/wwwdev.sh
new file mode 100755
index 00000000..fec74d9c
--- /dev/null
+++ b/scripts/wwwdev.sh
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+
+venv/bin/python3 -m deploy_chatmail.www
+
diff --git a/tests/chatmaild/test_newmail.py b/tests/chatmaild/test_newmail.py
index 5b6dad1f..8d13f981 100644
--- a/tests/chatmaild/test_newmail.py
+++ b/tests/chatmaild/test_newmail.py
@@ -3,6 +3,7 @@ import json
import chatmaild
from chatmaild.newemail import create_newemail_dict, print_new_account
+
def test_create_newemail_dict():
ac1 = create_newemail_dict(domain="example.org")
assert "@" in ac1["email"]
diff --git a/tests/online/test_0_qr.py b/tests/online/test_0_qr.py
index 2f4c5327..5c1a5865 100644
--- a/tests/online/test_0_qr.py
+++ b/tests/online/test_0_qr.py
@@ -1,5 +1,3 @@
-
-
from deploy_chatmail.genqr import gen_qr_png_data
diff --git a/tests/test_helpers.py b/tests/test_helpers.py
index 683ed435..ed0909d1 100644
--- a/tests/test_helpers.py
+++ b/tests/test_helpers.py
@@ -1,19 +1,10 @@
import textwrap
+import importlib.resources
-from deploy_chatmail import build_htmlj2_from_markdown, get_ini_settings
+from deploy_chatmail.www import build_webpages
+from deploy_chatmail import get_ini_settings
-
-def test_markdown(tmp_path):
- path = tmp_path.joinpath("privacy.md")
- path.write_text("# privacy policy")
- build_htmlj2_from_markdown(path)
- output = path.with_name("privacy.html.j2")
- assert output.exists()
- print(output.read_text())
-
-
-def test_get_settings(tmp_path):
- inipath = tmp_path.joinpath("chatmail.ini")
+def create_ini(inipath):
inipath.write_text(
textwrap.dedent(
"""\
@@ -30,10 +21,25 @@ def test_get_settings(tmp_path):
"""
)
)
+
+def test_build_webpages(tmp_path):
+ pkgroot = importlib.resources.files("deploy_chatmail")
+ src_dir = pkgroot.joinpath("../../../www/src").resolve()
+ assert src_dir.exists(), src_dir
+
+ inipath = tmp_path.joinpath("chatmail.ini")
+ create_ini(inipath)
+ config = get_ini_settings("example.org", inipath)
+ build_dir = tmp_path.joinpath("build")
+ build_webpages(src_dir, build_dir, config)
+
+
+def test_get_settings(tmp_path):
+ inipath = tmp_path.joinpath("chatmail.ini")
+ create_ini(inipath)
+
d = get_ini_settings("x.testrun.org", inipath)
assert d["privacy_postal"] == "address-line1\naddress-line2"
assert d["privacy_mail"] == "privacy@example.org"
assert d["privacy_pdo"] == "address-line3"
assert d["mail_domain"] == "x.testrun.org"
-
-
diff --git a/www/collage-info.png b/www/src/collage-info.png
similarity index 100%
rename from www/collage-info.png
rename to www/src/collage-info.png
diff --git a/www/collage-privacy.png b/www/src/collage-privacy.png
similarity index 100%
rename from www/collage-privacy.png
rename to www/src/collage-privacy.png
diff --git a/www/collage-top.png b/www/src/collage-top.png
similarity index 100%
rename from www/collage-top.png
rename to www/src/collage-top.png
diff --git a/www/index.md b/www/src/index.md
similarity index 100%
rename from www/index.md
rename to www/src/index.md
diff --git a/www/info.md b/www/src/info.md
similarity index 100%
rename from www/info.md
rename to www/src/info.md
diff --git a/www/src/page-layout.html b/www/src/page-layout.html
new file mode 100644
index 00000000..1c92d6e8
--- /dev/null
+++ b/www/src/page-layout.html
@@ -0,0 +1,17 @@
+
+
+
+
+ {{ config.mail_domain }} {{ pagename }}
+
+
+
+{{ markdown_html }}
+
+
+
diff --git a/www/privacy.md b/www/src/privacy.md
similarity index 100%
rename from www/privacy.md
rename to www/src/privacy.md
diff --git a/www/water.css b/www/src/water.css
similarity index 100%
rename from www/water.css
rename to www/src/water.css