#!/usr/bin/env python3 """Mini-CMS: MD-Dateien in md/ per Browseroberflaeche bearbeiten und als statische Seite (HTML + CSS) nach output/ exportieren. Nur Python-Standardbibliothek, kein Internet noetig. Aufruf: python3 cms.py -> Server + UI starten (http://localhost:8500) python3 cms.py export -> nur exportieren, kein Server python3 cms.py port 9000 -> Server auf anderem Port """ import json import os import re import shutil import sys import threading import webbrowser from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer from urllib.parse import unquote, urlparse ROOT = os.path.dirname(os.path.abspath(__file__)) MD_DIR = os.path.join(ROOT, "md") TPL_DIR = os.path.join(ROOT, "templates") CSS_DIR = os.path.join(ROOT, "css") OUT_DIR = os.path.join(ROOT, "output") PORT = 8500 for d in (MD_DIR, TPL_DIR, CSS_DIR, OUT_DIR): os.makedirs(d, exist_ok=True) SLUG_RE = re.compile(r"^[a-z0-9][a-z0-9_-]*$", re.IGNORECASE) START_PAGE = "startseite" FOOTER_PAGE = "footer" # Baustein: wird in jede Seite eingefuegt, nicht eigenstaendig exportiert # ---------------------------------------------------------------- markdown def md_to_html(text): """Minimaler Markdown->HTML-Konverter: Ueberschriften, **fett**, *kursiv*, `code`, [Links](url), Listen, Code-Bloecke, > Zitate, --- Trennlinien, Absaetze. """ lines = text.replace("\r\n", "\n").split("\n") out = [] i = 0 in_code = False in_list = False list_tag = None def inline(s): s = re.sub(r"`([^`]+)`", r"\1", s) s = re.sub(r"\*\*([^*]+)\*\*", r"\1", s) s = re.sub(r"\*([^*]+)\*", r"\1", s) s = re.sub(r"\[([^\]]+)\]\(([^)]+)\)", r'\1', s) return s def close_list(): nonlocal in_list, list_tag if in_list: out.append("" % list_tag) in_list = False list_tag = None while i < len(lines): line = lines[i] if line.strip().startswith("```"): close_list() if not in_code: out.append("
")
                in_code = True
            else:
                out.append("
") in_code = False i += 1 continue if in_code: out.append(_escape(line)) i += 1 continue stripped = line.strip() if not stripped: close_list() i += 1 continue m = re.match(r"^(#{1,6})\s+(.*)$", stripped) if m: close_list() level = len(m.group(1)) out.append("%s" % (level, inline(_escape(m.group(2))), level)) i += 1 continue if re.match(r"^-{3,}$", stripped): close_list() out.append("
") i += 1 continue m = re.match(r"^([-*+])\s+(.*)$", stripped) if m: if not in_list or list_tag != "ul": close_list() out.append("