Make base watermark scaling visible and interactive

This commit is contained in:
Terranom674
2026-08-15 15:49:09 +02:00
parent 21b736adb9
commit 724c9a061e

View File

@@ -4,8 +4,8 @@
{foreach from=$BRATONIEN_ERRORS item=error}<div class="errors"><p>{$error|escape:html}</p></div>{/foreach}
<style>
.titrePage h2 { color: #e6e6e6; }
.bratonien-admin { max-width: 1180px; margin: 0 auto; }
.titrePage h2 { color:#e6e6e6; }
.bratonien-admin { max-width:1180px; margin:0 auto; }
.bratonien-nav { display:flex; flex-wrap:wrap; gap:8px; margin:0 0 18px; }
.bratonien-nav a { display:inline-block; padding:8px 12px; border:1px solid rgba(255,255,255,.18); border-radius:4px; color:#d7d7d7; text-decoration:none; transition:color .15s ease,border-color .15s ease,background .15s ease; }
.bratonien-nav a:hover,.bratonien-nav a:focus { color:#f0a646; border-color:rgba(240,166,70,.65); background:rgba(240,166,70,.06); }
@@ -21,13 +21,7 @@
.bratonien-form-grid { display:grid; grid-template-columns:150px minmax(0,1fr); gap:10px 14px; align-items:center; }
.bratonien-form-grid > label,.bratonien-label { font-weight:600; }
.bratonien-inline { display:flex; flex-wrap:wrap; gap:8px 12px; align-items:center; }
.bratonien-actions { display:flex; flex-wrap:wrap; gap:8px; margin-top:14px; }
.bratonien-watermark-preview { display:grid; grid-template-columns:minmax(230px,320px) 1fr; gap:18px; align-items:center; }
.bratonien-watermark-preview__image,.bratonien-scale-preview__stage { display:flex; align-items:center; justify-content:center; padding:14px; border:1px solid rgba(255,255,255,.14); border-radius:4px; background:linear-gradient(45deg,rgba(255,255,255,.09) 25%,transparent 25%),linear-gradient(-45deg,rgba(255,255,255,.09) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(255,255,255,.09) 75%),linear-gradient(-45deg,transparent 75%,rgba(255,255,255,.09) 75%); background-size:20px 20px; background-position:0 0,0 10px,10px -10px,-10px 0; box-sizing:border-box; overflow:hidden; }
.bratonien-watermark-preview__image { min-height:150px; }
.bratonien-watermark-preview__image img { max-width:100%; max-height:190px; object-fit:contain; }
.bratonien-watermark-preview__empty { color:#a9a9a9; text-align:center; }
.bratonien-watermark-preview__meta { line-height:1.65; }
.bratonien-actions { display:flex; flex-wrap:wrap; gap:8px; margin-top:14px; align-items:center; }
.bratonien-profile-list { display:grid; gap:10px; }
.bratonien-profile { border:1px solid rgba(255,255,255,.12); border-radius:4px; background:rgba(0,0,0,.07); }
.bratonien-profile summary { cursor:pointer; padding:12px 14px; color:#d7d7d7; font-weight:600; }
@@ -36,22 +30,23 @@
.bratonien-profile__summary { margin-left:8px; color:#a9a9a9; font-weight:normal; }
.bratonien-scale-editor { margin-top:16px; padding-top:16px; border-top:1px solid rgba(255,255,255,.1); }
.bratonien-scale-editor__title { margin:0 0 10px; color:#d7d7d7; font-weight:700; }
.bratonien-scale-grid { display:grid; grid-template-columns:minmax(320px,1fr) minmax(300px,380px); gap:18px; align-items:start; }
.bratonien-scale-grid { display:grid; grid-template-columns:minmax(300px,1fr) minmax(280px,380px); gap:18px; align-items:start; }
.bratonien-scale-fields { display:grid; grid-template-columns:150px minmax(0,1fr); gap:10px 14px; align-items:center; }
.bratonien-scale-fields label { font-weight:600; }
.bratonien-scale-fields input[type=number] { width:95px; }
.bratonien-scale-preview__stage { width:100%; height:220px; padding:10px; }
.bratonien-scale-preview__stage { width:100%; height:250px; display:flex; align-items:center; justify-content:center; padding:10px; border:1px solid rgba(255,255,255,.14); border-radius:4px; background:linear-gradient(45deg,rgba(255,255,255,.09) 25%,transparent 25%),linear-gradient(-45deg,rgba(255,255,255,.09) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(255,255,255,.09) 75%),linear-gradient(-45deg,transparent 75%,rgba(255,255,255,.09) 75%); background-size:20px 20px; background-position:0 0,0 10px,10px -10px,-10px 0; box-sizing:border-box; overflow:hidden; }
.bratonien-scale-preview__stage img { display:block; max-width:none; max-height:none; object-fit:contain; }
.bratonien-scale-preview__empty { color:#a9a9a9; text-align:center; }
.bratonien-scale-preview__info { margin-top:8px; color:#a9a9a9; line-height:1.5; }
.bratonien-lock { color:#a9a9a9; }
.bratonien-base-note { margin:12px 0 0; color:#a9a9a9; font-size:12px; line-height:1.5; }
.bratonien-rule-table { width:100%; border-collapse:collapse; }
.bratonien-rule-table th,.bratonien-rule-table td { padding:9px 8px; text-align:left; vertical-align:middle; border-bottom:1px solid rgba(255,255,255,.09); }
.bratonien-rule-table th { color:#d7d7d7; }
.bratonien-effective { white-space:nowrap; }
.bratonien-muted { color:#a9a9a9; }
@media (max-width:850px) {
.bratonien-grid,.bratonien-watermark-preview,.bratonien-scale-grid { grid-template-columns:1fr; }
.bratonien-grid,.bratonien-scale-grid { grid-template-columns:1fr; }
.bratonien-form-grid,.bratonien-scale-fields { grid-template-columns:1fr; }
.bratonien-rule-table { display:block; overflow-x:auto; }
}
@@ -72,11 +67,7 @@
<div class="bratonien-card">
<h4>Engine</h4>
<div class="bratonien-status"><span class="bratonien-status__dot {if $WATERMARK_ENGINE.enabled}is-active{/if}"></span><strong>{if $WATERMARK_ENGINE.enabled}Aktiv{else}Inaktiv{/if}</strong></div>
<form method="post">
<input type="hidden" name="pwg_token" value="{$PWG_TOKEN|escape:html}">
<label><input type="checkbox" name="engine_enabled" value="1" {if $WATERMARK_ENGINE.enabled}checked{/if}> Bratonien-Wasserzeichenverwaltung aktivieren</label>
<div class="bratonien-actions"><button class="buttonLike" type="submit" name="bratonien_tool" value="watermark_engine">Status speichern</button></div>
</form>
<form method="post"><input type="hidden" name="pwg_token" value="{$PWG_TOKEN|escape:html}"><label><input type="checkbox" name="engine_enabled" value="1" {if $WATERMARK_ENGINE.enabled}checked{/if}> Bratonien-Wasserzeichenverwaltung aktivieren</label><div class="bratonien-actions"><button class="buttonLike" type="submit" name="bratonien_tool" value="watermark_engine">Status speichern</button></div></form>
</div>
<div class="bratonien-card">
<h4>Standardregeln</h4>
@@ -91,50 +82,50 @@
<section class="bratonien-section" id="wasserzeichen">
<h3>Wasserzeichen</h3>
<p class="bratonien-section__intro">Dateien, Profile und deren Größe an einer Stelle verwalten.</p>
<div class="bratonien-grid">
<div class="bratonien-card">
<h4>Aktuelles Wasserzeichen</h4>
<div class="bratonien-watermark-preview">
<div class="bratonien-watermark-preview__image">{if $WATERMARK.preview_url}<img src="{$WATERMARK.preview_url|escape:html}" alt="Aktuelles Wasserzeichen">{else}<div class="bratonien-watermark-preview__empty">Noch kein Wasserzeichen ausgewählt</div>{/if}</div>
<div class="bratonien-watermark-preview__meta">{if $WATERMARK.file}<strong>{$WATERMARK.file|escape:html}</strong><br>Position: {$WATERMARK.xpos} / {$WATERMARK.ypos}<br>Deckkraft: {$WATERMARK.opacity}%<br>Mindestgröße: {$WATERMARK.minw} × {$WATERMARK.minh}{else}Keine Datei ausgewählt{/if}</div>
<p class="bratonien-section__intro">Basis-Wasserzeichen direkt vorbereiten und anschließend bei Bedarf pro Profil abweichend konfigurieren.</p>
<form method="post" enctype="multipart/form-data" data-watermark-editor>
<input type="hidden" name="pwg_token" value="{$PWG_TOKEN|escape:html}">
<div class="bratonien-grid">
<div class="bratonien-card">
<h4>Vorschau des Basis-Wasserzeichens</h4>
<div class="bratonien-scale-preview__stage" data-preview-stage>{if $WATERMARK.preview_url}<img src="{$WATERMARK.preview_url|escape:html}" alt="Vorschau" data-preview-image>{else}<span class="bratonien-scale-preview__empty" data-preview-empty>Keine Wasserzeichendatei gewählt</span><img src="" alt="Vorschau" data-preview-image style="display:none">{/if}</div>
<div class="bratonien-scale-preview__info" data-preview-info></div>
<p class="bratonien-base-note">Diese Größe ist die Basis für neue Profile. Bereits vorhandene Profile behalten ihre eigene Skalierung.</p>
</div>
</div>
<div class="bratonien-card">
<h4>Datei auswählen oder hochladen</h4>
<form method="post" enctype="multipart/form-data">
<input type="hidden" name="pwg_token" value="{$PWG_TOKEN|escape:html}">
<div class="bratonien-card">
<h4>Datei und Basisgröße</h4>
<div class="bratonien-form-grid">
<label for="watermark_file">Vorhandene Datei</label>
<select id="watermark_file" name="watermark_file">{foreach from=$WATERMARK_OPTIONS item=option}<option value="{$option.file|escape:html}" {if $option.file == $WATERMARK.file}selected{/if}>{$option.name|escape:html}</option>{/foreach}</select>
<select id="watermark_file" name="watermark_file" data-watermark-file>{foreach from=$WATERMARK_OPTIONS item=option}<option value="{$option.file|escape:html}" data-width="{$option.width}" data-height="{$option.height}" data-url="{$option.url|escape:html}" {if $option.file == $WATERMARK.file}selected{/if}>{$option.name|escape:html}</option>{/foreach}</select>
<label for="watermark_upload">Neues PNG</label><input id="watermark_upload" type="file" name="watermark_upload" accept="image/png">
<span class="bratonien-label">Originalgröße</span><span data-original-size>{if $WATERMARK.original_width}{$WATERMARK.original_width} × {$WATERMARK.original_height} px{else}Keine Datei gewählt{/if}</span>
<label for="watermark_scale_percent">Skalierung</label><span><input id="watermark_scale_percent" type="number" name="watermark_scale_percent" value="{$WATERMARK.scale_percent}" min="1" max="1000" step="0.1" data-scale-percent> %</span>
<label>Breite</label><span><input type="number" min="1" step="1" data-scale-width> px</span>
<label>Höhe</label><span><input type="number" min="1" step="1" data-scale-height> px</span>
<span class="bratonien-label">Seitenverhältnis</span><span class="bratonien-lock">🔒 gesperrt</span>
<span class="bratonien-label">Position</span><span class="bratonien-inline">X <input type="number" name="watermark_xpos" value="{$WATERMARK.xpos}" min="0" max="100" size="4"> Y <input type="number" name="watermark_ypos" value="{$WATERMARK.ypos}" min="0" max="100" size="4"></span>
<label for="watermark_opacity">Deckkraft</label><span><input id="watermark_opacity" type="number" name="watermark_opacity" value="{$WATERMARK.opacity}" min="1" max="100" size="4"> %</span>
<label for="watermark_opacity">Deckkraft</label><span><input id="watermark_opacity" type="number" name="watermark_opacity" value="{$WATERMARK.opacity}" min="1" max="100" size="4" data-watermark-opacity> %</span>
<span class="bratonien-label">Mindestgröße</span><span class="bratonien-inline"><input type="number" name="watermark_minw" value="{$WATERMARK.minw}" min="0" size="5"> × <input type="number" name="watermark_minh" value="{$WATERMARK.minh}" min="0" size="5"></span>
</div>
<div class="bratonien-actions"><label><input type="checkbox" name="watermark_clear_cache" value="1"> Bildcache danach leeren</label><button class="buttonLike" type="submit" name="bratonien_tool" value="watermark_save">Wasserzeichen speichern</button></div>
</form>
<div class="bratonien-actions"><label><input type="checkbox" name="watermark_clear_cache" value="1"> Bildcache danach leeren</label><button class="buttonLike" type="submit" name="bratonien_tool" value="watermark_save">Basis-Wasserzeichen speichern</button></div>
</div>
</div>
</div>
</form>
<div class="bratonien-card" style="margin-top:16px;">
<h4>Profile</h4>
<p class="bratonien-muted">Die Wasserzeichengröße wird pro Profil festgelegt. Prozent, Breite und Höhe sind miteinander gekoppelt; das Seitenverhältnis bleibt immer erhalten.</p>
<p class="bratonien-muted">Profile können die Basisgröße bewusst überschreiben. Prozent, Breite und Höhe bleiben dabei immer miteinander gekoppelt.</p>
<div class="bratonien-profile-list">
{foreach from=$WATERMARK_PROFILES item=profile}
<details class="bratonien-profile">
<summary>{$profile.name|escape:html}<span class="bratonien-profile__summary">{if $profile.active}aktiv{else}inaktiv{/if} · {$profile.opacity}% · {$profile.scale_percent}% Größe</span></summary>
<div class="bratonien-profile__body">
<form method="post" data-watermark-editor>
<input type="hidden" name="pwg_token" value="{$PWG_TOKEN|escape:html}">
<input type="hidden" name="profile_id" value="{$profile.id}">
<input type="hidden" name="pwg_token" value="{$PWG_TOKEN|escape:html}"><input type="hidden" name="profile_id" value="{$profile.id}">
<div class="bratonien-form-grid">
<label>Name</label><span class="bratonien-inline"><input name="profile_name" value="{$profile.name|escape:html}" size="28"> <label><input type="checkbox" name="profile_active" value="1" {if $profile.active}checked{/if}> aktiv</label></span>
<label>Datei</label>
<select name="profile_file" data-watermark-file>
<option value="" data-width="0" data-height="0" data-url="">Keine Datei</option>
{foreach from=$WATERMARK_OPTIONS item=option}<option value="{$option.file|escape:html}" data-width="{$option.width}" data-height="{$option.height}" data-url="{$option.url|escape:html}" {if $option.file == $profile.watermark_file}selected{/if}>{$option.name|escape:html}</option>{/foreach}
</select>
<label>Datei</label><select name="profile_file" data-watermark-file><option value="" data-width="0" data-height="0" data-url="">Keine Datei</option>{foreach from=$WATERMARK_OPTIONS item=option}<option value="{$option.file|escape:html}" data-width="{$option.width}" data-height="{$option.height}" data-url="{$option.url|escape:html}" {if $option.file == $profile.watermark_file}selected{/if}>{$option.name|escape:html}</option>{/foreach}</select>
<span class="bratonien-label">Position</span><span class="bratonien-inline">X <input type="number" name="profile_xpos" value="{$profile.xpos}" min="0" max="100" size="3"> Y <input type="number" name="profile_ypos" value="{$profile.ypos}" min="0" max="100" size="3"></span>
<span class="bratonien-label">Wiederholen</span><span class="bratonien-inline">X <input type="number" name="profile_xrepeat" value="{$profile.xrepeat}" min="0" max="20" size="3"> Y <input type="number" name="profile_yrepeat" value="{$profile.yrepeat}" min="0" max="20" size="3"></span>
<label>Deckkraft</label><span><input type="number" name="profile_opacity" value="{$profile.opacity}" min="1" max="100" size="3" data-watermark-opacity> %</span>
@@ -150,17 +141,10 @@
<label>Höhe</label><span><input type="number" min="1" step="1" data-scale-height> px</span>
<span class="bratonien-label">Seitenverhältnis</span><span class="bratonien-lock">🔒 gesperrt</span>
</div>
<div>
<div class="bratonien-scale-preview__stage" data-preview-stage>{if $profile.preview_url}<img src="{$profile.preview_url|escape:html}" alt="Vorschau" data-preview-image>{else}<span class="bratonien-scale-preview__empty" data-preview-empty>Keine Wasserzeichendatei gewählt</span><img src="" alt="Vorschau" data-preview-image style="display:none">{/if}</div>
<div class="bratonien-scale-preview__info" data-preview-info></div>
</div>
<div><div class="bratonien-scale-preview__stage" data-preview-stage>{if $profile.preview_url}<img src="{$profile.preview_url|escape:html}" alt="Vorschau" data-preview-image>{else}<span class="bratonien-scale-preview__empty" data-preview-empty>Keine Wasserzeichendatei gewählt</span><img src="" alt="Vorschau" data-preview-image style="display:none">{/if}</div><div class="bratonien-scale-preview__info" data-preview-info></div></div>
</div>
</div>
<div class="bratonien-actions">
<button class="buttonLike" type="submit" name="bratonien_tool" value="watermark_profile_save">Speichern</button>
<button class="buttonLike" type="submit" name="bratonien_tool" value="watermark_profile_duplicate">Duplizieren</button>
<button class="buttonLike" type="submit" name="bratonien_tool" value="watermark_profile_delete" onclick="return confirm('Profil wirklich löschen?');">Löschen</button>
</div>
<div class="bratonien-actions"><button class="buttonLike" type="submit" name="bratonien_tool" value="watermark_profile_save">Speichern</button><button class="buttonLike" type="submit" name="bratonien_tool" value="watermark_profile_duplicate">Duplizieren</button><button class="buttonLike" type="submit" name="bratonien_tool" value="watermark_profile_delete" onclick="return confirm('Profil wirklich löschen?');">Löschen</button></div>
</form>
</div>
</details>
@@ -173,11 +157,7 @@
<input type="hidden" name="pwg_token" value="{$PWG_TOKEN|escape:html}">
<div class="bratonien-form-grid">
<label>Name</label><span class="bratonien-inline"><input name="profile_name" size="28"> <label><input type="checkbox" name="profile_active" value="1" checked> aktiv</label></span>
<label>Datei</label>
<select name="profile_file" data-watermark-file>
<option value="" data-width="0" data-height="0" data-url="">Keine Datei</option>
{foreach from=$WATERMARK_OPTIONS item=option}<option value="{$option.file|escape:html}" data-width="{$option.width}" data-height="{$option.height}" data-url="{$option.url|escape:html}">{$option.name|escape:html}</option>{/foreach}
</select>
<label>Datei</label><select name="profile_file" data-watermark-file><option value="" data-width="0" data-height="0" data-url="">Keine Datei</option>{foreach from=$WATERMARK_OPTIONS item=option}<option value="{$option.file|escape:html}" data-width="{$option.width}" data-height="{$option.height}" data-url="{$option.url|escape:html}">{$option.name|escape:html}</option>{/foreach}</select>
<span class="bratonien-label">Position</span><span class="bratonien-inline">X <input type="number" name="profile_xpos" value="90" min="0" max="100" size="3"> Y <input type="number" name="profile_ypos" value="90" min="0" max="100" size="3"></span>
<span class="bratonien-label">Wiederholen</span><span class="bratonien-inline">X <input type="number" name="profile_xrepeat" value="0" min="0" max="20" size="3"> Y <input type="number" name="profile_yrepeat" value="0" min="0" max="20" size="3"></span>
<label>Deckkraft</label><span><input type="number" name="profile_opacity" value="35" min="1" max="100" size="3" data-watermark-opacity> %</span>
@@ -188,15 +168,12 @@
<div class="bratonien-scale-grid">
<div class="bratonien-scale-fields">
<span class="bratonien-label">Originalgröße</span><span data-original-size>Keine Datei gewählt</span>
<label>Skalierung</label><span><input type="number" name="profile_scale_percent" value="100" min="1" max="1000" step="0.1" data-scale-percent> %</span>
<label>Skalierung</label><span><input type="number" name="profile_scale_percent" value="{$WATERMARK.scale_percent}" min="1" max="1000" step="0.1" data-scale-percent> %</span>
<label>Breite</label><span><input type="number" min="1" step="1" data-scale-width> px</span>
<label>Höhe</label><span><input type="number" min="1" step="1" data-scale-height> px</span>
<span class="bratonien-label">Seitenverhältnis</span><span class="bratonien-lock">🔒 gesperrt</span>
</div>
<div>
<div class="bratonien-scale-preview__stage" data-preview-stage><span class="bratonien-scale-preview__empty" data-preview-empty>Keine Wasserzeichendatei gewählt</span><img src="" alt="Vorschau" data-preview-image style="display:none"></div>
<div class="bratonien-scale-preview__info" data-preview-info></div>
</div>
<div><div class="bratonien-scale-preview__stage" data-preview-stage><span class="bratonien-scale-preview__empty" data-preview-empty>Keine Wasserzeichendatei gewählt</span><img src="" alt="Vorschau" data-preview-image style="display:none"></div><div class="bratonien-scale-preview__info" data-preview-info></div></div>
</div>
</div>
<div class="bratonien-actions"><button class="buttonLike" type="submit" name="bratonien_tool" value="watermark_profile_save">Profil anlegen</button></div>
@@ -208,150 +185,14 @@
</section>
<section class="bratonien-section" id="regeln">
<h3>Regeln</h3>
<p class="bratonien-section__intro">Standardregeln und Album-Ausnahmen gemeinsam verwalten.</p>
<div class="bratonien-card">
<h4>Globale Standardregeln</h4>
<form method="post">
<input type="hidden" name="pwg_token" value="{$PWG_TOKEN|escape:html}">
<div class="bratonien-form-grid">
<label>Öffentliche Alben</label><select name="public_profile"><option value="">Kein Wasserzeichen</option>{foreach from=$WATERMARK_PROFILES item=profile}{if $profile.active}<option value="{$profile.id}" {if $WATERMARK_DEFAULTS.public_profile == $profile.id}selected{/if}>{$profile.name|escape:html}</option>{/if}{/foreach}</select>
<label>Private Alben</label><select name="private_profile"><option value="">Kein Wasserzeichen</option>{foreach from=$WATERMARK_PROFILES item=profile}{if $profile.active}<option value="{$profile.id}" {if $WATERMARK_DEFAULTS.private_profile == $profile.id}selected{/if}>{$profile.name|escape:html}</option>{/if}{/foreach}</select>
</div>
<div class="bratonien-actions"><button class="buttonLike" type="submit" name="bratonien_tool" value="watermark_defaults">Standardregeln speichern</button></div>
</form>
</div>
<div class="bratonien-card" style="margin-top:16px;">
<h4>Album-Ausnahmen</h4>
<p class="bratonien-muted">Erben verwendet die nächste explizite Regel eines Elternalbums und danach den globalen Standard.</p>
<table class="bratonien-rule-table">
<thead><tr><th>Album</th><th>Sichtbarkeit</th><th>Regel</th><th>Profil</th><th>Wirksam</th><th></th></tr></thead>
<tbody>{foreach from=$WATERMARK_CATEGORIES item=category}<tr><form method="post"><input type="hidden" name="pwg_token" value="{$PWG_TOKEN|escape:html}"><input type="hidden" name="category_id" value="{$category.id}"><td><strong>{$category.display_name|escape:html}</strong></td><td>{$category.status|escape:html}</td><td><select name="rule_mode"><option value="inherit" {if $category.rule.mode == 'inherit'}selected{/if}>Erben</option><option value="disabled" {if $category.rule.mode == 'disabled'}selected{/if}>Kein Wasserzeichen</option><option value="profile" {if $category.rule.mode == 'profile'}selected{/if}>Profil verwenden</option></select></td><td><select name="rule_profile"><option value="">Profil wählen</option>{foreach from=$WATERMARK_PROFILES item=profile}{if $profile.active}<option value="{$profile.id}" {if $category.rule.profile_id == $profile.id}selected{/if}>{$profile.name|escape:html}</option>{/if}{/foreach}</select></td><td class="bratonien-effective"><strong>{$category.effective_label|escape:html}</strong> <span class="bratonien-muted">({$category.effective.source|escape:html})</span></td><td><button class="buttonLike" type="submit" name="bratonien_tool" value="watermark_rule">Speichern</button></td></form></tr>{/foreach}</tbody>
</table>
</div>
<h3>Regeln</h3><p class="bratonien-section__intro">Standardregeln und Album-Ausnahmen gemeinsam verwalten.</p>
<div class="bratonien-card"><h4>Globale Standardregeln</h4><form method="post"><input type="hidden" name="pwg_token" value="{$PWG_TOKEN|escape:html}"><div class="bratonien-form-grid"><label>Öffentliche Alben</label><select name="public_profile"><option value="">Kein Wasserzeichen</option>{foreach from=$WATERMARK_PROFILES item=profile}{if $profile.active}<option value="{$profile.id}" {if $WATERMARK_DEFAULTS.public_profile == $profile.id}selected{/if}>{$profile.name|escape:html}</option>{/if}{/foreach}</select><label>Private Alben</label><select name="private_profile"><option value="">Kein Wasserzeichen</option>{foreach from=$WATERMARK_PROFILES item=profile}{if $profile.active}<option value="{$profile.id}" {if $WATERMARK_DEFAULTS.private_profile == $profile.id}selected{/if}>{$profile.name|escape:html}</option>{/if}{/foreach}</select></div><div class="bratonien-actions"><button class="buttonLike" type="submit" name="bratonien_tool" value="watermark_defaults">Standardregeln speichern</button></div></form></div>
<div class="bratonien-card" style="margin-top:16px;"><h4>Album-Ausnahmen</h4><p class="bratonien-muted">Erben verwendet die nächste explizite Regel eines Elternalbums und danach den globalen Standard.</p><table class="bratonien-rule-table"><thead><tr><th>Album</th><th>Sichtbarkeit</th><th>Regel</th><th>Profil</th><th>Wirksam</th><th></th></tr></thead><tbody>{foreach from=$WATERMARK_CATEGORIES item=category}<tr><form method="post"><input type="hidden" name="pwg_token" value="{$PWG_TOKEN|escape:html}"><input type="hidden" name="category_id" value="{$category.id}"><td><strong>{$category.display_name|escape:html}</strong></td><td>{$category.status|escape:html}</td><td><select name="rule_mode"><option value="inherit" {if $category.rule.mode == 'inherit'}selected{/if}>Erben</option><option value="disabled" {if $category.rule.mode == 'disabled'}selected{/if}>Kein Wasserzeichen</option><option value="profile" {if $category.rule.mode == 'profile'}selected{/if}>Profil verwenden</option></select></td><td><select name="rule_profile"><option value="">Profil wählen</option>{foreach from=$WATERMARK_PROFILES item=profile}{if $profile.active}<option value="{$profile.id}" {if $category.rule.profile_id == $profile.id}selected{/if}>{$profile.name|escape:html}</option>{/if}{/foreach}</select></td><td class="bratonien-effective"><strong>{$category.effective_label|escape:html}</strong> <span class="bratonien-muted">({$category.effective.source|escape:html})</span></td><td><button class="buttonLike" type="submit" name="bratonien_tool" value="watermark_rule">Speichern</button></td></form></tr>{/foreach}</tbody></table></div>
</section>
<section class="bratonien-section" id="wartung">
<h3>Wartung</h3>
<p class="bratonien-section__intro">Werkzeuge, die nicht zur täglichen Konfiguration gehören.</p>
<div class="bratonien-card"><h4>Bildcache</h4><p>Löscht alle erzeugten Piwigo- und Bratonien-Bildderivate. Originalbilder bleiben erhalten.</p><form method="post"><input type="hidden" name="pwg_token" value="{$PWG_TOKEN|escape:html}"><button class="buttonLike" type="submit" name="bratonien_tool" value="image_cache_clear" onclick="return confirm('Wirklich den gesamten Bildcache leeren?');">Bildcache leeren</button></form></div>
</section>
<section class="bratonien-section" id="wartung"><h3>Wartung</h3><p class="bratonien-section__intro">Werkzeuge, die nicht zur täglichen Konfiguration gehören.</p><div class="bratonien-card"><h4>Bildcache</h4><p>Löscht alle erzeugten Piwigo- und Bratonien-Bildderivate. Originalbilder bleiben erhalten.</p><form method="post"><input type="hidden" name="pwg_token" value="{$PWG_TOKEN|escape:html}"><button class="buttonLike" type="submit" name="bratonien_tool" value="image_cache_clear" onclick="return confirm('Wirklich den gesamten Bildcache leeren?');">Bildcache leeren</button></form></div></section>
</div>
<script>
(function () {
'use strict';
function clamp(value, min, max) {
return Math.min(max, Math.max(min, value));
}
function round(value, digits) {
var factor = Math.pow(10, digits || 0);
return Math.round(value * factor) / factor;
}
function initWatermarkEditor(form) {
var fileSelect = form.querySelector('[data-watermark-file]');
var percentInput = form.querySelector('[data-scale-percent]');
var widthInput = form.querySelector('[data-scale-width]');
var heightInput = form.querySelector('[data-scale-height]');
var originalLabel = form.querySelector('[data-original-size]');
var previewImage = form.querySelector('[data-preview-image]');
var previewEmpty = form.querySelector('[data-preview-empty]');
var previewInfo = form.querySelector('[data-preview-info]');
var opacityInput = form.querySelector('[data-watermark-opacity]');
var previewStage = form.querySelector('[data-preview-stage]');
if (!fileSelect || !percentInput || !widthInput || !heightInput) return;
function selectedMeta() {
var option = fileSelect.options[fileSelect.selectedIndex];
return {
width: parseFloat(option ? option.getAttribute('data-width') : 0) || 0,
height: parseFloat(option ? option.getAttribute('data-height') : 0) || 0,
url: option ? (option.getAttribute('data-url') || '') : ''
};
}
function updatePreview(width, height, percent) {
var meta = selectedMeta();
if (!meta.width || !meta.height || !meta.url) {
if (previewImage) previewImage.style.display = 'none';
if (previewEmpty) previewEmpty.style.display = '';
if (previewInfo) previewInfo.textContent = '';
return;
}
if (previewEmpty) previewEmpty.style.display = 'none';
if (previewImage) {
previewImage.src = meta.url;
previewImage.style.display = 'block';
previewImage.style.opacity = opacityInput ? String(clamp((parseFloat(opacityInput.value) || 100) / 100, 0.01, 1)) : '1';
var availableWidth = Math.max(1, (previewStage ? previewStage.clientWidth : 360) - 20);
var availableHeight = Math.max(1, (previewStage ? previewStage.clientHeight : 220) - 20);
var visualScale = Math.min(1, availableWidth / width, availableHeight / height);
previewImage.style.width = Math.max(1, Math.round(width * visualScale)) + 'px';
previewImage.style.height = Math.max(1, Math.round(height * visualScale)) + 'px';
}
if (previewInfo) {
var fitted = width > 340 || height > 200;
previewInfo.textContent = 'Original: ' + Math.round(meta.width) + ' × ' + Math.round(meta.height) + ' px · Ziel: ' + Math.round(width) + ' × ' + Math.round(height) + ' px · ' + round(percent, 1) + ' %' + (fitted ? ' · Vorschau verkleinert' : '');
}
}
function applyPercent(rawPercent) {
var meta = selectedMeta();
if (!meta.width || !meta.height) {
widthInput.value = '';
heightInput.value = '';
originalLabel.textContent = 'Keine Datei gewählt';
updatePreview(1, 1, 100);
return;
}
var percent = parseFloat(rawPercent);
if (!isFinite(percent)) percent = 100;
percent = clamp(percent, 1, 1000);
var width = Math.max(1, Math.round(meta.width * percent / 100));
var height = Math.max(1, Math.round(meta.height * percent / 100));
percentInput.value = round(percent, 2);
widthInput.value = width;
heightInput.value = height;
originalLabel.textContent = Math.round(meta.width) + ' × ' + Math.round(meta.height) + ' px';
updatePreview(width, height, percent);
}
function applyWidth() {
var meta = selectedMeta();
if (!meta.width || !meta.height) return;
var width = parseFloat(widthInput.value);
if (!isFinite(width) || width < 1) width = meta.width;
var percent = clamp(width / meta.width * 100, 1, 1000);
applyPercent(percent);
}
function applyHeight() {
var meta = selectedMeta();
if (!meta.width || !meta.height) return;
var height = parseFloat(heightInput.value);
if (!isFinite(height) || height < 1) height = meta.height;
var percent = clamp(height / meta.height * 100, 1, 1000);
applyPercent(percent);
}
fileSelect.addEventListener('change', function () { applyPercent(percentInput.value || 100); });
percentInput.addEventListener('input', function () { applyPercent(percentInput.value); });
widthInput.addEventListener('input', applyWidth);
heightInput.addEventListener('input', applyHeight);
if (opacityInput) opacityInput.addEventListener('input', function () { applyPercent(percentInput.value); });
applyPercent(percentInput.value || 100);
}
document.querySelectorAll('[data-watermark-editor]').forEach(initWatermarkEditor);
})();
(function(){'use strict';function clamp(v,min,max){return Math.min(max,Math.max(min,v));}function round(v,d){var f=Math.pow(10,d||0);return Math.round(v*f)/f;}function initWatermarkEditor(form){var fileSelect=form.querySelector('[data-watermark-file]'),percentInput=form.querySelector('[data-scale-percent]'),widthInput=form.querySelector('[data-scale-width]'),heightInput=form.querySelector('[data-scale-height]'),originalLabel=form.querySelector('[data-original-size]'),previewImage=form.querySelector('[data-preview-image]'),previewEmpty=form.querySelector('[data-preview-empty]'),previewInfo=form.querySelector('[data-preview-info]'),opacityInput=form.querySelector('[data-watermark-opacity]'),previewStage=form.querySelector('[data-preview-stage]');if(!fileSelect||!percentInput||!widthInput||!heightInput)return;function selectedMeta(){var option=fileSelect.options[fileSelect.selectedIndex];return{width:parseFloat(option?option.getAttribute('data-width'):0)||0,height:parseFloat(option?option.getAttribute('data-height'):0)||0,url:option?(option.getAttribute('data-url')||''):''};}function updatePreview(width,height,percent){var meta=selectedMeta();if(!meta.width||!meta.height||!meta.url){if(previewImage)previewImage.style.display='none';if(previewEmpty)previewEmpty.style.display='';if(previewInfo)previewInfo.textContent='';return;}if(previewEmpty)previewEmpty.style.display='none';if(previewImage){previewImage.src=meta.url;previewImage.style.display='block';previewImage.style.opacity=opacityInput?String(clamp((parseFloat(opacityInput.value)||100)/100,.01,1)):'1';var aw=Math.max(1,(previewStage?previewStage.clientWidth:360)-20),ah=Math.max(1,(previewStage?previewStage.clientHeight:250)-20),vs=Math.min(1,aw/width,ah/height);previewImage.style.width=Math.max(1,Math.round(width*vs))+'px';previewImage.style.height=Math.max(1,Math.round(height*vs))+'px';}if(previewInfo){var aW=Math.max(1,(previewStage?previewStage.clientWidth:360)-20),aH=Math.max(1,(previewStage?previewStage.clientHeight:250)-20),fitted=width>aW||height>aH;previewInfo.textContent='Original: '+Math.round(meta.width)+' × '+Math.round(meta.height)+' px · Ziel: '+Math.round(width)+' × '+Math.round(height)+' px · '+round(percent,1)+' %'+(fitted?' · Vorschau verkleinert':'');}}function applyPercent(raw){var meta=selectedMeta();if(!meta.width||!meta.height){widthInput.value='';heightInput.value='';if(originalLabel)originalLabel.textContent='Keine Datei gewählt';updatePreview(1,1,100);return;}var p=parseFloat(raw);if(!isFinite(p))p=100;p=clamp(p,1,1000);var w=Math.max(1,Math.round(meta.width*p/100)),h=Math.max(1,Math.round(meta.height*p/100));percentInput.value=round(p,2);widthInput.value=w;heightInput.value=h;if(originalLabel)originalLabel.textContent=Math.round(meta.width)+' × '+Math.round(meta.height)+' px';updatePreview(w,h,p);}function applyWidth(){var meta=selectedMeta();if(!meta.width||!meta.height)return;var w=parseFloat(widthInput.value);if(!isFinite(w)||w<1)w=meta.width;applyPercent(clamp(w/meta.width*100,1,1000));}function applyHeight(){var meta=selectedMeta();if(!meta.width||!meta.height)return;var h=parseFloat(heightInput.value);if(!isFinite(h)||h<1)h=meta.height;applyPercent(clamp(h/meta.height*100,1,1000));}fileSelect.addEventListener('change',function(){applyPercent(percentInput.value||100);});percentInput.addEventListener('input',function(){applyPercent(percentInput.value);});widthInput.addEventListener('input',applyWidth);heightInput.addEventListener('input',applyHeight);if(opacityInput)opacityInput.addEventListener('input',function(){applyPercent(percentInput.value);});applyPercent(percentInput.value||100);}document.querySelectorAll('[data-watermark-editor]').forEach(initWatermarkEditor);})();
</script>