Fix des Fehlers des Foldings der Planner Vorschau
This commit is contained in:
@@ -2,3 +2,5 @@ public/
|
|||||||
resources/
|
resources/
|
||||||
.hugo_build.lock
|
.hugo_build.lock
|
||||||
.env
|
.env
|
||||||
|
.deploy_manifest.json
|
||||||
|
__pycache__/
|
||||||
|
|||||||
@@ -62,6 +62,24 @@ Link an den Kunden geht.
|
|||||||
|
|
||||||
## 6. Zugriff beschränken: nur du + der Kunde
|
## 6. Zugriff beschränken: nur du + der Kunde
|
||||||
|
|
||||||
|
> **Wichtige Einschränkung (Stand: getestet):** Das Bunny-Dashboard zeigt in der
|
||||||
|
> Security-Seite der Pull Zone nur die **Basic** Token Authentication (Felder:
|
||||||
|
> "Token authentication", "Token IP validation", "Url token authentication Key").
|
||||||
|
> Diese Basic-Methode signiert immer einen **exakten Pfad** – es gibt dabei
|
||||||
|
> **kein** Path-Prefix-/Wildcard-/Verzeichnis-Token. Ein Token für `/` ist damit
|
||||||
|
> nur für `/` gültig, nicht für `/ferienwohnungen/` oder andere Unterseiten
|
||||||
|
> (führt zu 403 auf allen anderen Seiten). Verzeichnis-weite Tokens gibt es bei
|
||||||
|
> Bunny nur über die separate "Advanced" Token-Methode (HS256, i. d. R. anderer
|
||||||
|
> Tarif/Reiter) – die in diesem Projekt bisher nicht verfügbar/eingerichtet ist.
|
||||||
|
>
|
||||||
|
> **Für die aktuelle Vorschau-Phase deshalb:** Token Authentication ist
|
||||||
|
> deaktiviert. Die `b-cdn.net`-Test-URL ist damit frei erreichbar (kein
|
||||||
|
> Passwortschutz, aber auch nicht in Suchmaschinen o. ä. auffindbar/verlinkt) –
|
||||||
|
> ausreichend für eine kurze, informelle Abstimmung mit dem Kunden. Die
|
||||||
|
> folgenden Abschnitte 6.1–6.3 bleiben als Referenz stehen, falls später doch
|
||||||
|
> ein Advanced-Token-Setup eingerichtet wird oder Bunny die Basic-Methode um
|
||||||
|
> Prefix-Matching erweitert.
|
||||||
|
|
||||||
Bunny.net Pull Zones bieten dafür **Token Authentication**: Requests an die
|
Bunny.net Pull Zones bieten dafür **Token Authentication**: Requests an die
|
||||||
Pull Zone werden nur ausgeliefert, wenn die URL einen gültigen, signierten Token
|
Pull Zone werden nur ausgeliefert, wenn die URL einen gültigen, signierten Token
|
||||||
enthält. Es gibt keinen klassischen Basic-Auth-Login-Dialog im Browser –
|
enthält. Es gibt keinen klassischen Basic-Auth-Login-Dialog im Browser –
|
||||||
@@ -77,14 +95,16 @@ festgelegten Ablaufzeitpunkt gültig ist.
|
|||||||
diesen Key kennt, kann selbst gültige Links erzeugen.
|
diesen Key kennt, kann selbst gültige Links erzeugen.
|
||||||
|
|
||||||
Ab jetzt liefert die Pull Zone **nichts mehr aus**, ohne gültigen Token in der
|
Ab jetzt liefert die Pull Zone **nichts mehr aus**, ohne gültigen Token in der
|
||||||
URL – das ist der eigentliche Zugriffsschutz.
|
URL – das ist der eigentliche Zugriffsschutz. Mit der Basic-Methode gilt das
|
||||||
|
aber **pro exaktem Pfad** (siehe Hinweis oben) – für mehrere Seiten braucht es
|
||||||
|
entsprechend mehrere Links, siehe 6.2.
|
||||||
|
|
||||||
### 6.2 Signierten Link erzeugen
|
### 6.2 Signierten Link je Seite erzeugen
|
||||||
|
|
||||||
Ein signierter Link besteht aus einem Hash über `Authentication Key + Pfad +
|
Ein signierter Link besteht aus einem Hash über `Authentication Key + Pfad +
|
||||||
Ablaufzeitpunkt`. Das lässt sich einfach per kleinem Python-Snippet erzeugen,
|
Ablaufzeitpunkt`. Da die Basic-Methode keine Verzeichnis-Tokens kennt, braucht
|
||||||
z. B. lokal ausführen, wenn ein neuer Vorschau-Link für den Kunden gebraucht
|
jede Seite ihren eigenen Link. Lokal ausführen, wenn neue Vorschau-Links für
|
||||||
wird:
|
den Kunden gebraucht werden:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import hashlib
|
import hashlib
|
||||||
@@ -92,28 +112,44 @@ import base64
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
security_key = "DEIN_AUTHENTICATION_KEY"
|
security_key = "DEIN_AUTHENTICATION_KEY"
|
||||||
url_path = "/index.html" # z.B. Startseite, oder "/" je nach Setup
|
|
||||||
expires = int(time.time()) + 7 * 24 * 60 * 60 # 7 Tage gültig
|
expires = int(time.time()) + 7 * 24 * 60 * 60 # 7 Tage gültig
|
||||||
|
|
||||||
hashable = f"{security_key}{url_path}{expires}"
|
# Alle Pfade eintragen, die der Kunde ansehen soll:
|
||||||
token_hash = hashlib.sha256(hashable.encode()).digest()
|
url_paths = [
|
||||||
token = base64.urlsafe_b64encode(token_hash).decode().rstrip("=")
|
"/",
|
||||||
|
"/ferienwohnungen/",
|
||||||
|
"/ferienwohnungen/fewo1/",
|
||||||
|
"/ferienwohnungen/fewo2/",
|
||||||
|
"/ferienwohnungen/fewo3/",
|
||||||
|
"/preise/",
|
||||||
|
"/ausflugsziele/",
|
||||||
|
"/kontakt/",
|
||||||
|
"/impressum/",
|
||||||
|
"/datenschutz/",
|
||||||
|
]
|
||||||
|
|
||||||
print(f"https://iwz-ferienwohnungen.b-cdn.net{url_path}?token={token}&expires={expires}")
|
for url_path in url_paths:
|
||||||
|
hashable = f"{security_key}{url_path}{expires}"
|
||||||
|
token_hash = hashlib.sha256(hashable.encode()).digest()
|
||||||
|
token = base64.urlsafe_b64encode(token_hash).decode().rstrip("=")
|
||||||
|
print(f"https://iwz-ferienwohnungen.b-cdn.net{url_path}?token={token}&expires={expires}")
|
||||||
```
|
```
|
||||||
|
|
||||||
Den ausgegebenen Link schickst du dem Kunden. Läuft die Gültigkeit ab, einfach
|
Die ausgegebenen Links schickst du dem Kunden (z. B. als Liste). Läuft die
|
||||||
ein neues Snippet mit neuem `expires`-Wert ausführen und den frischen Link
|
Gültigkeit ab, einfach das Snippet mit neuem `expires`-Wert erneut ausführen.
|
||||||
verschicken.
|
|
||||||
|
|
||||||
### 6.3 Praktisch für Vorschau-Zwecke
|
### 6.3 Praktisch für Vorschau-Zwecke
|
||||||
|
|
||||||
- Länge der Gültigkeit (`expires`) nach Bedarf wählen – z. B. 7 Tage für eine
|
- Länge der Gültigkeit (`expires`) nach Bedarf wählen – z. B. 7 Tage für eine
|
||||||
kurze Abstimmungsrunde, oder wenige Stunden für einen einzelnen Termin.
|
kurze Abstimmungsrunde, oder wenige Stunden für einen einzelnen Termin.
|
||||||
- Ohne gültigen Token bekommt jeder andere Besucher einen Fehler von Bunny –
|
- Ohne gültigen Token bekommt jeder andere Besucher einen Fehler von Bunny –
|
||||||
die Seite ist damit faktisch nur über den geteilten Link erreichbar.
|
die Seite ist damit faktisch nur über die geteilten Links erreichbar.
|
||||||
- Sobald die Seite live gehen soll (echte Domain, öffentlich), wird Token
|
- Da pro Seite ein eigener Token nötig ist, taugt dieser Weg nur für wenige,
|
||||||
Authentication in der Pull Zone einfach wieder deaktiviert.
|
fest bekannte Seiten – nicht für eine frei navigierbare Vorschau. Für eine
|
||||||
|
frei klickbare Vorschau (aktueller Stand) bleibt Token Authentication
|
||||||
|
vorerst deaktiviert (siehe Hinweis oben).
|
||||||
|
- Sobald die Seite live gehen soll (echte Domain, öffentlich), bleibt Token
|
||||||
|
Authentication in der Pull Zone ohnehin deaktiviert.
|
||||||
|
|
||||||
## 7. Domain-Anbindung (später, für den echten Umzug)
|
## 7. Domain-Anbindung (später, für den echten Umzug)
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ umfeld:
|
|||||||
- "Sonnenschirm"
|
- "Sonnenschirm"
|
||||||
- titel: "Entfernungen"
|
- titel: "Entfernungen"
|
||||||
punkte:
|
punkte:
|
||||||
|
- "PKW-Stellplatz: 10 m"
|
||||||
- "Arzt: 200 m"
|
- "Arzt: 200 m"
|
||||||
- "Bushaltestelle: 250 m"
|
- "Bushaltestelle: 250 m"
|
||||||
- "Supermarkt: 0,8 km"
|
- "Supermarkt: 0,8 km"
|
||||||
@@ -44,7 +45,6 @@ umfeld:
|
|||||||
punkte:
|
punkte:
|
||||||
- "Tischtennis"
|
- "Tischtennis"
|
||||||
- "Spielplatz"
|
- "Spielplatz"
|
||||||
- "PKW-Stellplatz"
|
|
||||||
gemeinsam:
|
gemeinsam:
|
||||||
- titel: "Wohnen und Leben"
|
- titel: "Wohnen und Leben"
|
||||||
punkte:
|
punkte:
|
||||||
|
|||||||
+4
-4
@@ -7,15 +7,15 @@ description: "Unsere Preise und Buchungsbedingungen"
|
|||||||
|
|
||||||
| Wohnung | Größe | Personen | Preis/Nacht |
|
| Wohnung | Größe | Personen | Preis/Nacht |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| Ferienwohnung 1 | 45 qm | bis 4 Pers. | ab 60 € |
|
| [Ferienwohnung 1](/ferienwohnungen/fewo1/) | 45 qm | bis 4 Pers. | ab 60 € |
|
||||||
| Ferienwohnung 2 | 40 qm | bis 3 Pers. | ab 55 € |
|
| [Ferienwohnung 2](/ferienwohnungen/fewo2/) | 40 qm | bis 3 Pers. | ab 55 € |
|
||||||
| Ferienwohnung 3 | 56 qm | bis 5 Pers. | ab 100 € |
|
| [Ferienwohnung 3](/ferienwohnungen/fewo3/) | 56 qm | bis 5 Pers. | ab 100 € |
|
||||||
|
|
||||||
Jede weitere Person: **15 € / Nacht** · Kinder unter 12 Jahren: **12 € / Nacht**
|
Jede weitere Person: **15 € / Nacht** · Kinder unter 12 Jahren: **12 € / Nacht**
|
||||||
|
|
||||||
## Im Preis enthalten
|
## Im Preis enthalten
|
||||||
|
|
||||||
Endreinigung, Bettwäsche und Handtücher sind im Preis inbegriffen. WLAN ist kostenlos.
|
**Endreinigung, Bettwäsche** und **Handtücher** sind _im Preis inbegriffen_. WLAN ist _kostenlos_.
|
||||||
|
|
||||||
## Buchungsbedingungen
|
## Buchungsbedingungen
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
@echo off
|
||||||
|
echo.
|
||||||
|
echo === IWZ Ferienwohnungen - Deploy (nur Änderungen) ===
|
||||||
|
echo.
|
||||||
|
python deploy_lastdiff.py
|
||||||
|
if %errorlevel% neq 0 (
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
pause
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
IWZ Ferienwohnungen - Deploy nur geaenderter Dateien
|
||||||
|
Baut die Hugo-Seite und laedt per FTP zur Bunny.net Storage Zone nur die
|
||||||
|
Dateien hoch, die sich seit dem letzten Deploy mit diesem Skript geaendert
|
||||||
|
haben (Vergleich per Datei-Hash gegen ein lokales Manifest).
|
||||||
|
"""
|
||||||
|
|
||||||
|
import ftplib
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
PROJECT_ROOT = Path(__file__).resolve().parent
|
||||||
|
PUBLIC_DIR = PROJECT_ROOT / "public"
|
||||||
|
MANIFEST_PATH = PROJECT_ROOT / ".deploy_manifest.json"
|
||||||
|
FTP_PORT = 21
|
||||||
|
|
||||||
|
|
||||||
|
def load_env(path):
|
||||||
|
env = {}
|
||||||
|
if not path.exists():
|
||||||
|
return env
|
||||||
|
for line in path.read_text(encoding="utf-8").splitlines():
|
||||||
|
line = line.strip()
|
||||||
|
if not line or line.startswith("#") or "=" not in line:
|
||||||
|
continue
|
||||||
|
key, value = line.split("=", 1)
|
||||||
|
env[key.strip()] = value.strip()
|
||||||
|
return env
|
||||||
|
|
||||||
|
|
||||||
|
def hugo_build():
|
||||||
|
print("\n-> Hugo Build...")
|
||||||
|
result = subprocess.run("hugo --gc --minify", shell=True)
|
||||||
|
if result.returncode != 0:
|
||||||
|
print("\nFEHLER: Hugo Build fehlgeschlagen!")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def hash_file(path):
|
||||||
|
h = hashlib.sha256()
|
||||||
|
with open(path, "rb") as f:
|
||||||
|
for chunk in iter(lambda: f.read(65536), b""):
|
||||||
|
h.update(chunk)
|
||||||
|
return h.hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def scan_public():
|
||||||
|
manifest = {}
|
||||||
|
for entry in PUBLIC_DIR.rglob("*"):
|
||||||
|
if entry.is_file():
|
||||||
|
rel = entry.relative_to(PUBLIC_DIR).as_posix()
|
||||||
|
manifest[rel] = hash_file(entry)
|
||||||
|
return manifest
|
||||||
|
|
||||||
|
|
||||||
|
def load_manifest():
|
||||||
|
if not MANIFEST_PATH.exists():
|
||||||
|
return {}
|
||||||
|
return json.loads(MANIFEST_PATH.read_text(encoding="utf-8"))
|
||||||
|
|
||||||
|
|
||||||
|
def save_manifest(manifest):
|
||||||
|
MANIFEST_PATH.write_text(json.dumps(manifest, indent=2, sort_keys=True), encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def diff_manifest(old, new):
|
||||||
|
changed = [rel for rel, digest in new.items() if old.get(rel) != digest]
|
||||||
|
deleted = [rel for rel in old if rel not in new]
|
||||||
|
return sorted(changed), sorted(deleted)
|
||||||
|
|
||||||
|
|
||||||
|
def ftp_mkdirs(ftp, remote_dir):
|
||||||
|
parts = remote_dir.split("/")
|
||||||
|
path = ""
|
||||||
|
for part in parts:
|
||||||
|
if not part:
|
||||||
|
continue
|
||||||
|
path = f"{path}/{part}" if path else part
|
||||||
|
try:
|
||||||
|
ftp.mkd(path)
|
||||||
|
except ftplib.error_perm:
|
||||||
|
pass # existiert bereits
|
||||||
|
|
||||||
|
|
||||||
|
def ftp_upload_files(ftp, rel_paths):
|
||||||
|
for rel in rel_paths:
|
||||||
|
local_path = PUBLIC_DIR / rel
|
||||||
|
remote_dir = "/".join(rel.split("/")[:-1])
|
||||||
|
if remote_dir:
|
||||||
|
ftp_mkdirs(ftp, remote_dir)
|
||||||
|
with open(local_path, "rb") as f:
|
||||||
|
ftp.storbinary(f"STOR {rel}", f)
|
||||||
|
print(f" {rel}")
|
||||||
|
|
||||||
|
|
||||||
|
def ftp_delete_files(ftp, rel_paths):
|
||||||
|
for rel in rel_paths:
|
||||||
|
try:
|
||||||
|
ftp.delete(rel)
|
||||||
|
print(f" gelöscht: {rel}")
|
||||||
|
except ftplib.error_perm as e:
|
||||||
|
print(f" konnte nicht löschen: {rel} ({e})")
|
||||||
|
|
||||||
|
|
||||||
|
def deploy_to_bunny(host, zone, password, changed, deleted):
|
||||||
|
print(f"\n-> Deploy zu Bunny.net Storage Zone '{zone}'...")
|
||||||
|
with ftplib.FTP() as ftp:
|
||||||
|
ftp.connect(host, FTP_PORT)
|
||||||
|
ftp.login(zone, password)
|
||||||
|
ftp.set_pasv(True)
|
||||||
|
if changed:
|
||||||
|
print(f"\n-> Lade {len(changed)} geänderte Datei(en) hoch:")
|
||||||
|
ftp_upload_files(ftp, changed)
|
||||||
|
if deleted:
|
||||||
|
print(f"\n-> Entferne {len(deleted)} gelöschte Datei(en):")
|
||||||
|
ftp_delete_files(ftp, deleted)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print("=== IWZ Deploy (nur Änderungen) ===")
|
||||||
|
|
||||||
|
env = {**load_env(PROJECT_ROOT / ".env"), **os.environ}
|
||||||
|
zone = env.get("BUNNY_STORAGE_ZONE")
|
||||||
|
host = env.get("BUNNY_STORAGE_HOST", "storage.bunnycdn.com")
|
||||||
|
password = env.get("BUNNY_STORAGE_PASSWORD")
|
||||||
|
|
||||||
|
if not zone or not password:
|
||||||
|
print("\nFEHLER: BUNNY_STORAGE_ZONE / BUNNY_STORAGE_PASSWORD fehlen.")
|
||||||
|
print("Siehe .env.example -> .env kopieren und befuellen.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
hugo_build()
|
||||||
|
|
||||||
|
old_manifest = load_manifest()
|
||||||
|
new_manifest = scan_public()
|
||||||
|
changed, deleted = diff_manifest(old_manifest, new_manifest)
|
||||||
|
|
||||||
|
if not changed and not deleted:
|
||||||
|
print("\nKeine Änderungen seit dem letzten Deploy. Nichts hochzuladen.")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
deploy_to_bunny(host, zone, password, changed, deleted)
|
||||||
|
|
||||||
|
save_manifest(new_manifest)
|
||||||
|
print("\nFertig! Änderungen sind live.")
|
||||||
@@ -17,14 +17,14 @@
|
|||||||
<img src="{{ . }}" alt="{{ $.Title }}" class="fewo-card-img">
|
<img src="{{ . }}" alt="{{ $.Title }}" class="fewo-card-img">
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<div class="fewo-card-body">
|
<div class="fewo-card-body">
|
||||||
<h2><a href="{{ .Permalink }}">{{ .Title }}</a></h2>
|
<h2><a href="{{ .RelPermalink }}">{{ .Title }}</a></h2>
|
||||||
<p class="fewo-meta">
|
<p class="fewo-meta">
|
||||||
{{ with .Params.groesse }}<span>{{ . }}</span>{{ end }}
|
{{ with .Params.groesse }}<span>{{ . }}</span>{{ end }}
|
||||||
{{ with .Params.personen }}<span>bis {{ . }} Personen</span>{{ end }}
|
{{ with .Params.personen }}<span>bis {{ . }} Personen</span>{{ end }}
|
||||||
{{ with .Params.preis }}<span>ab {{ . }} € / Nacht</span>{{ end }}
|
{{ with .Params.preis }}<span>ab {{ . }} € / Nacht</span>{{ end }}
|
||||||
</p>
|
</p>
|
||||||
{{ with .Params.teaser }}<p>{{ . }}</p>{{ end }}
|
{{ with .Params.teaser }}<p>{{ . }}</p>{{ end }}
|
||||||
<a href="{{ .Permalink }}" class="btn btn-primary">Mehr erfahren</a>
|
<a href="{{ .RelPermalink }}" class="btn btn-primary">Mehr erfahren</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|||||||
+11
-11
@@ -12,7 +12,7 @@
|
|||||||
<div class="welcome-text">{{ .Content }}</div>
|
<div class="welcome-text">{{ .Content }}</div>
|
||||||
<p class="welcome-cta">
|
<p class="welcome-cta">
|
||||||
<a href="/ferienwohnungen/" class="btn btn-primary">Unsere Wohnungen</a>
|
<a href="/ferienwohnungen/" class="btn btn-primary">Unsere Wohnungen</a>
|
||||||
<a href="/preise/" class="btn btn-outline">Preise & Buchung</a>
|
<a href="/preise/" class="btn btn-outline">Preise</a>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -51,23 +51,23 @@
|
|||||||
<h2>Unsere Wohnungen</h2>
|
<h2>Unsere Wohnungen</h2>
|
||||||
<div class="fewo-grid">
|
<div class="fewo-grid">
|
||||||
{{ $pages := where .Site.RegularPages "Section" "ferienwohnungen" }}
|
{{ $pages := where .Site.RegularPages "Section" "ferienwohnungen" }}
|
||||||
{{ range $pages }}
|
{{ range $page := $pages }}
|
||||||
<div class="fewo-card">
|
<div class="fewo-card">
|
||||||
{{ $vorschau := .Params.vorschau }}
|
{{ $vorschau := $page.Params.vorschau }}
|
||||||
{{ if and (not $vorschau) .Params.bilder }}{{ $vorschau = index .Params.bilder 0 }}{{ end }}
|
{{ if and (not $vorschau) $page.Params.bilder }}{{ $vorschau = index $page.Params.bilder 0 }}{{ end }}
|
||||||
{{ with $vorschau }}
|
{{ with $vorschau }}
|
||||||
<a href="{{ $.Permalink }}" class="fewo-card-imglink">
|
<a href="{{ $page.RelPermalink }}" class="fewo-card-imglink">
|
||||||
<img src="{{ . }}" alt="{{ $.Title }}" class="fewo-card-img" loading="lazy">
|
<img src="{{ . }}" alt="{{ $page.Title }}" class="fewo-card-img" loading="lazy">
|
||||||
</a>
|
</a>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<div class="fewo-card-body">
|
<div class="fewo-card-body">
|
||||||
<h3><a href="{{ .Permalink }}">{{ .Title }}</a></h3>
|
<h3><a href="{{ $page.RelPermalink }}">{{ $page.Title }}</a></h3>
|
||||||
<p class="fewo-meta">
|
<p class="fewo-meta">
|
||||||
{{ with .Params.groesse }}<span>{{ . }}</span>{{ end }}
|
{{ with $page.Params.groesse }}<span>{{ . }}</span>{{ end }}
|
||||||
{{ with .Params.personen }}<span>bis {{ . }} Personen</span>{{ end }}
|
{{ with $page.Params.personen }}<span>bis {{ . }} Personen</span>{{ end }}
|
||||||
{{ with .Params.preis }}<span>ab {{ . }} € / Nacht</span>{{ end }}
|
{{ with $page.Params.preis }}<span>ab {{ . }} € / Nacht</span>{{ end }}
|
||||||
</p>
|
</p>
|
||||||
<a href="{{ .Permalink }}" class="btn">Details & Buchung</a>
|
<a href="{{ $page.RelPermalink }}" class="btn btn-primary">Details & Buchung</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|||||||
@@ -317,9 +317,6 @@ img { max-width: 100%; height: auto; display: block; }
|
|||||||
gap: 1.25rem;
|
gap: 1.25rem;
|
||||||
}
|
}
|
||||||
@media (max-width: 760px) {
|
@media (max-width: 760px) {
|
||||||
.spec-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
||||||
}
|
|
||||||
@media (max-width: 480px) {
|
|
||||||
.spec-grid { grid-template-columns: 1fr; }
|
.spec-grid { grid-template-columns: 1fr; }
|
||||||
}
|
}
|
||||||
.spec-block {
|
.spec-block {
|
||||||
|
|||||||
+1
-1
@@ -36,11 +36,11 @@ if (belBtn && belFrame) {
|
|||||||
const willShow = belFrame.hidden;
|
const willShow = belFrame.hidden;
|
||||||
if (willShow && !loaded) {
|
if (willShow && !loaded) {
|
||||||
const iframe = document.createElement('iframe');
|
const iframe = document.createElement('iframe');
|
||||||
iframe.src = belFrame.dataset.src;
|
|
||||||
iframe.width = '100%';
|
iframe.width = '100%';
|
||||||
iframe.height = '225';
|
iframe.height = '225';
|
||||||
iframe.frameBorder = '0';
|
iframe.frameBorder = '0';
|
||||||
belFrame.appendChild(iframe);
|
belFrame.appendChild(iframe);
|
||||||
|
iframe.src = belFrame.dataset.src;
|
||||||
loaded = true;
|
loaded = true;
|
||||||
}
|
}
|
||||||
belFrame.hidden = !willShow;
|
belFrame.hidden = !willShow;
|
||||||
|
|||||||
Reference in New Issue
Block a user