228 lines
11 KiB
Python
228 lines
11 KiB
Python
import glob
|
|
import os
|
|
import re
|
|
import sys
|
|
|
|
THEME_REPO_URL = "https://git.falkenthal.me/falkenthal/pretix-falkenthal-theme"
|
|
PLATFORM_IMPRINT_URL = "https://tickets.falkenthal.me/impressum"
|
|
PLATFORM_PRIVACY_URL = "https://tickets.falkenthal.me/datenschutz"
|
|
ASSET_VERSION = "1.8.5"
|
|
|
|
control_files = [
|
|
'/pretix/src/pretix/control/templates/pretixcontrol/base.html',
|
|
'/pretix/src/pretix/control/templates/pretixcontrol/auth/base.html',
|
|
]
|
|
|
|
platform_footer_html = (
|
|
'<div class="falkenthal-platform-bar">'
|
|
'<div class="falkenthal-platform-info">'
|
|
'<span>Technischer Plattformbetrieb: <strong class="ft-brand">FALKENTHAL</strong></span>'
|
|
'<span class="ft-sep">·</span>'
|
|
f'<a href="{PLATFORM_IMPRINT_URL}" target="_blank" rel="noopener">Plattform-Impressum</a>'
|
|
'<span class="ft-sep">·</span>'
|
|
f'<a href="{PLATFORM_PRIVACY_URL}" target="_blank" rel="noopener">Plattform-Datenschutz</a>'
|
|
'<span class="ft-sep">·</span>'
|
|
f'<a href="{THEME_REPO_URL}" target="_blank" rel="noopener">Theme & Quellcode (AGPLv3)</a>'
|
|
'</div>'
|
|
'</div>'
|
|
'</footer>'
|
|
)
|
|
|
|
# 1. Control & Auth Templates patchen (Plattform-Backend: immer Falkenthal-Branding)
|
|
for p in control_files:
|
|
if not os.path.exists(p):
|
|
print(f"ERROR: Expected template not found: {p}", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
with open(p, 'r', encoding='utf-8') as f:
|
|
c = f.read()
|
|
|
|
# Standard-Pretix-Favicon im Backend durch Falkenthal-Favicon ersetzen
|
|
c = re.sub(r'<link[^>]*rel=[\"\'](shortcut )?icon[\"\'][^>]*>', '', c, flags=re.I)
|
|
|
|
# Eindeutige Body-Klasse für Auth/Login-Seiten setzen (verhindert CSS-Leaks im normalen Backend)
|
|
if p.endswith('auth/base.html') and 'falkenthal-auth-body' not in c:
|
|
if '<body' in c:
|
|
c = re.sub(
|
|
r'<body([^>]*)class=["\']([^"\']*)["\']',
|
|
r'<body\1class="\2 falkenthal-auth-body"',
|
|
c,
|
|
count=1,
|
|
flags=re.I,
|
|
)
|
|
if 'falkenthal-auth-body' not in c:
|
|
c = re.sub(r'<body', '<body class="falkenthal-auth-body"', c, count=1, flags=re.I)
|
|
|
|
if '/static/whitelabel/control.css' not in c:
|
|
if '</head>' not in c:
|
|
print(f"ERROR: </head> tag not found in {p}", file=sys.stderr)
|
|
sys.exit(1)
|
|
c = c.replace(
|
|
'</head>',
|
|
f'<link rel="icon" type="image/svg+xml" href="/static/whitelabel/favicon.svg?v={ASSET_VERSION}">'
|
|
f'<link rel="shortcut icon" href="/static/whitelabel/favicon.svg?v={ASSET_VERSION}">'
|
|
f'<link rel="stylesheet" type="text/css" href="/static/whitelabel/control.css?v={ASSET_VERSION}"></head>'
|
|
)
|
|
|
|
if 'falkenthal-platform-bar' not in c:
|
|
if '</footer>' not in c:
|
|
print(f"ERROR: </footer> tag not found in {p}", file=sys.stderr)
|
|
sys.exit(1)
|
|
c = c.replace('</footer>', platform_footer_html)
|
|
|
|
with open(p, 'w', encoding='utf-8') as f:
|
|
f.write(c)
|
|
|
|
# 2. Presale Template patchen (Ticketshop: Veranstalter-Favicons respektieren, Fallback = Falkenthal)
|
|
presale_file = '/pretix/src/pretix/presale/templates/pretixpresale/base.html'
|
|
if not os.path.exists(presale_file):
|
|
print(f"ERROR: Expected template not found: {presale_file}", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
with open(presale_file, 'r', encoding='utf-8') as f:
|
|
c = f.read()
|
|
|
|
# Nur die statischen Standard-Pretix-Favicon-Fallbacks ersetzen, damit individuelle
|
|
# Veranstalter-/Event-Favicons ({% if request.event.settings.favicon %}) erhalten bleiben!
|
|
c = re.sub(
|
|
r'<link[^>]*rel=[\"\'](shortcut )?icon[\"\'][^>]*static\s+[\"\']pretixbase/img/favicon[^\"\']*[\"\'][^>]*>',
|
|
'',
|
|
c,
|
|
flags=re.I,
|
|
)
|
|
# Falls in einem {% else %}-Zweig der Standard-Favicon-Block nun leer wäre oder direkt vor </head>:
|
|
if '/static/whitelabel/presale.css' not in c:
|
|
if '</head>' not in c:
|
|
print(f"ERROR: </head> tag not found in {presale_file}", file=sys.stderr)
|
|
sys.exit(1)
|
|
presale_head_inject = (
|
|
'{% if not request.event or not request.event.settings.favicon %}'
|
|
f'<link rel="icon" type="image/svg+xml" href="/static/whitelabel/favicon.svg?v={ASSET_VERSION}">'
|
|
f'<link rel="shortcut icon" href="/static/whitelabel/favicon.svg?v={ASSET_VERSION}">'
|
|
'{% endif %}'
|
|
f'<link rel="stylesheet" type="text/css" href="/static/whitelabel/presale.css?v={ASSET_VERSION}"></head>'
|
|
)
|
|
c = c.replace('</head>', presale_head_inject)
|
|
|
|
if 'falkenthal-platform-bar' not in c:
|
|
if '</footer>' not in c:
|
|
print(f"ERROR: </footer> tag not found in {presale_file}", file=sys.stderr)
|
|
sys.exit(1)
|
|
c = c.replace('</footer>', platform_footer_html)
|
|
|
|
with open(presale_file, 'w', encoding='utf-8') as f:
|
|
f.write(c)
|
|
|
|
# 3. Globale Fehlerseiten (400, 403, 404, 500, 503) mit Plattform-Rechtstexten ausstatten
|
|
error_templates_dir = '/pretix/src/pretix/base/templates'
|
|
for err_name in ['400.html', '403.html', '403_csrf.html', '404.html', '500.html', '503.html']:
|
|
err_path = os.path.join(error_templates_dir, err_name)
|
|
if os.path.exists(err_path):
|
|
with open(err_path, 'r', encoding='utf-8') as f:
|
|
ec = f.read()
|
|
if 'falkenthal-platform-bar' not in ec and '</body>' in ec:
|
|
error_footer = (
|
|
'<div class="falkenthal-platform-bar" style="margin-top:32px;padding:16px 12px;'
|
|
'border-top:1px solid rgba(148,163,184,0.25);font-family:system-ui,-apple-system,sans-serif;'
|
|
'font-size:12px;color:#64748b;text-align:center;line-height:1.6;">'
|
|
'<span>Technischer Plattformbetrieb: <strong style="color:#334155;">FALKENTHAL</strong></span>'
|
|
' · '
|
|
f'<a href="{PLATFORM_IMPRINT_URL}" target="_blank" rel="noopener" style="color:#64748b;text-decoration:underline;">Plattform-Impressum</a>'
|
|
' · '
|
|
f'<a href="{PLATFORM_PRIVACY_URL}" target="_blank" rel="noopener" style="color:#64748b;text-decoration:underline;">Plattform-Datenschutz</a>'
|
|
' · '
|
|
f'<a href="{THEME_REPO_URL}" target="_blank" rel="noopener" style="color:#64748b;text-decoration:underline;">Theme & Quellcode (AGPLv3)</a>'
|
|
'</div></body>'
|
|
)
|
|
ec = ec.replace('</body>', error_footer)
|
|
with open(err_path, 'w', encoding='utf-8') as f:
|
|
f.write(ec)
|
|
|
|
# 4. Eingebettetes Pretix-Widget (/widget/v1.*.js & widget.py):
|
|
# Einheitliche Plattform-Pflichtangaben garantiert in jedem <pretix-widget> (auch auf Jimdo & bei leeren Events)!
|
|
widget_platform_bar_js = r"""
|
|
;(function(){
|
|
if (typeof document === 'undefined' || window.__falkenthalWidgetPatched) return;
|
|
window.__falkenthalWidgetPatched = true;
|
|
var BAR_HTML = '<span>Technischer Plattformbetrieb: <strong style="font-weight:600;">FALKENTHAL</strong></span>' +
|
|
' · <a href="https://tickets.falkenthal.me/impressum" target="_blank" rel="noopener" style="color:inherit;text-decoration:underline;">Plattform-Impressum</a>' +
|
|
' · <a href="https://tickets.falkenthal.me/datenschutz" target="_blank" rel="noopener" style="color:inherit;text-decoration:underline;">Plattform-Datenschutz</a>' +
|
|
' · <a href="https://git.falkenthal.me/falkenthal/pretix-falkenthal-theme" target="_blank" rel="noopener" style="color:inherit;text-decoration:underline;">Theme & Quellcode (AGPLv3)</a>';
|
|
function injectWidgetBar() {
|
|
var containers = document.querySelectorAll('.pretix-widget-attribution, .pretix-widget');
|
|
for (var i = 0; i < containers.length; i++) {
|
|
var el = containers[i];
|
|
var widgetRoot = el.classList.contains('pretix-widget') ? el : el.closest('.pretix-widget');
|
|
var checkTarget = widgetRoot || el;
|
|
if (checkTarget.querySelector('.pretix-widget-falkenthal-bar')) continue;
|
|
var target = checkTarget.querySelector('.pretix-widget-attribution') || checkTarget;
|
|
var bar = document.createElement('div');
|
|
bar.className = 'pretix-widget-falkenthal-bar';
|
|
bar.style.cssText = 'margin-top:8px;padding-top:8px;border-top:1px solid rgba(128,128,128,0.22);font-size:11px;line-height:1.5;text-align:center;opacity:0.85;clear:both;';
|
|
bar.innerHTML = BAR_HTML;
|
|
target.appendChild(bar);
|
|
}
|
|
}
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', injectWidgetBar);
|
|
} else {
|
|
injectWidgetBar();
|
|
}
|
|
if (typeof MutationObserver !== 'undefined') {
|
|
var obs = new MutationObserver(function() { injectWidgetBar(); });
|
|
var startObs = function() {
|
|
if (document.body) obs.observe(document.body, { childList: true, subtree: true });
|
|
};
|
|
if (document.body) startObs();
|
|
else document.addEventListener('DOMContentLoaded', startObs);
|
|
}
|
|
setInterval(injectWidgetBar, 1000);
|
|
})();
|
|
"""
|
|
|
|
# 4a. Alle statischen Widget-JS-Dateien im gesamten Pretix-Baum finden (z.B. ui/widget.js, widget/*.js, static.dist)
|
|
widget_js_candidates = (
|
|
glob.glob('/pretix/src/pretix/presale/static/**/*widget*.js', recursive=True)
|
|
+ glob.glob('/pretix/src/pretix/static.dist/**/*widget*.js', recursive=True)
|
|
)
|
|
patched_js_count = 0
|
|
for wjs in set(widget_js_candidates):
|
|
if os.path.isfile(wjs) and not wjs.endswith('.min.js.map'):
|
|
with open(wjs, 'r', encoding='utf-8') as f:
|
|
wc = f.read()
|
|
if '__falkenthalWidgetPatched' not in wc:
|
|
with open(wjs, 'a', encoding='utf-8') as f:
|
|
f.write('\n' + widget_platform_bar_js)
|
|
patched_js_count += 1
|
|
|
|
# 4b. Zusätzlich direkt die Django-View /pretix/src/pretix/presale/views/widget.py patchen,
|
|
# da Pretix /widget/v1.de.js dynamisch über diese View generiert, im Dateisystem speichert und ausliefert!
|
|
widget_view_py = '/pretix/src/pretix/presale/views/widget.py'
|
|
if os.path.exists(widget_view_py):
|
|
with open(widget_view_py, 'r', encoding='utf-8') as f:
|
|
wpy = f.read()
|
|
if '__falkenthalWidgetPatched' not in wpy:
|
|
wpy_patch = (
|
|
f"\n\n# --- FALKENTHAL PLATFORM LEGAL BAR INJECTION ---\n"
|
|
f"_FALKENTHAL_WIDGET_JS = {repr(widget_platform_bar_js)}\n"
|
|
f"if 'generate_widget_js' in globals():\n"
|
|
f" _orig_generate_widget_js = generate_widget_js\n"
|
|
f" def generate_widget_js(lang):\n"
|
|
f" code = _orig_generate_widget_js(lang)\n"
|
|
f" if '__falkenthalWidgetPatched' not in code:\n"
|
|
f" code = code + '\\n' + _FALKENTHAL_WIDGET_JS\n"
|
|
f" return code\n"
|
|
f"if 'widget_js' in globals():\n"
|
|
f" _orig_widget_js = widget_js\n"
|
|
f" def widget_js(request, *args, **kwargs):\n"
|
|
f" resp = _orig_widget_js(request, *args, **kwargs)\n"
|
|
f" if hasattr(resp, 'content') and b'__falkenthalWidgetPatched' not in resp.content:\n"
|
|
f" resp.content = resp.content + _FALKENTHAL_WIDGET_JS.encode('utf-8')\n"
|
|
f" return resp\n"
|
|
)
|
|
with open(widget_view_py, 'a', encoding='utf-8') as f:
|
|
f.write(wpy_patch)
|
|
|
|
print(f"Pretix templates and widget assets ({patched_js_count} JS files + widget.py) patched and verified successfully (v{ASSET_VERSION}).")
|
|
|