|
34 | 34 | {{- $controlsBelow := eq $args.controlsPlacement "bottom" -}} |
35 | 35 |
|
36 | 36 | {{ if not $error }} |
| 37 | + {{/* Build-time detection of iframe embedding restrictions */}} |
| 38 | + {{- $showFallback := $args.showFallback -}} |
| 39 | + {{- if and $args.url (not $showFallback) -}} |
| 40 | + {{- with try (resources.GetRemote $args.url (dict |
| 41 | + "method" "HEAD" |
| 42 | + "responseHeaders" (slice "X-Frame-Options" "Content-Security-Policy") |
| 43 | + )) -}} |
| 44 | + {{- if not .Err -}} |
| 45 | + {{- $headers := .Value.Data.Headers -}} |
| 46 | + {{- with index $headers "X-Frame-Options" -}} |
| 47 | + {{- $showFallback = true -}} |
| 48 | + {{- partial "utilities/LogWarn.html" (dict |
| 49 | + "partial" "assets/preview.html" |
| 50 | + "warnid" "warn-preview-restricted" |
| 51 | + "msg" "iframe embedding blocked" |
| 52 | + "details" (slice (printf "URL '%s' sets X-Frame-Options; showing fallback" $args.url)) |
| 53 | + "file" page.File |
| 54 | + ) -}} |
| 55 | + {{- end -}} |
| 56 | + {{- range index $headers "Content-Security-Policy" -}} |
| 57 | + {{- if findRE `frame-ancestors\s+'none'` . -}} |
| 58 | + {{- $showFallback = true -}} |
| 59 | + {{- partial "utilities/LogWarn.html" (dict |
| 60 | + "partial" "assets/preview.html" |
| 61 | + "warnid" "warn-preview-restricted" |
| 62 | + "msg" "iframe embedding blocked" |
| 63 | + "details" (slice (printf "URL '%s' sets CSP frame-ancestors 'none'; showing fallback" $args.url)) |
| 64 | + "file" page.File |
| 65 | + ) -}} |
| 66 | + {{- end -}} |
| 67 | + {{- end -}} |
| 68 | + {{- else -}} |
| 69 | + {{- if $showFallback -}} |
| 70 | + {{- partial "utilities/LogWarn.html" (dict |
| 71 | + "partial" "assets/preview.html" |
| 72 | + "warnid" "warn-preview-restricted" |
| 73 | + "msg" "HEAD request failed" |
| 74 | + "details" (slice (printf "Could not check '%s' for embedding restrictions; fallback shown due to show-fallback setting" $args.url)) |
| 75 | + "file" page.File |
| 76 | + ) -}} |
| 77 | + {{- end -}} |
| 78 | + {{- end -}} |
| 79 | + {{- end -}} |
| 80 | + {{- end -}} |
| 81 | + |
37 | 82 | {{/* Optional section heading */}} |
38 | 83 | {{ if $args.heading.title }} |
39 | 84 | {{ partial "assets/section-title.html" (dict |
|
42 | 87 | {{ end }} |
43 | 88 |
|
44 | 89 | {{/* Device selection button group — rendered above or below the preview */}} |
45 | | - {{- if not $controlsBelow }} |
| 90 | + {{- if and (not $showFallback) (not $controlsBelow) }} |
46 | 91 | <div class="container-xxl"> |
47 | 92 | <div class="preview-controls d-flex justify-content-center mb-3"> |
48 | 93 | <div class="btn-group" role="tablist" aria-label="Device selection"> |
|
75 | 120 | role="tabpanel" |
76 | 121 | aria-labelledby="{{ $id }}-{{ $device }}-tab"> |
77 | 122 | <iframe src="{{ $args.url }}" |
78 | | - class="preview-iframe preview-{{ $device }}" |
| 123 | + class="preview-iframe preview-{{ $device }}{{ if $showFallback }} d-none{{ end }}" |
79 | 124 | title="{{ or $args.heading.title (printf "Preview of %s" $args.url) }}" |
80 | 125 | loading="lazy" |
81 | 126 | allow="storage-access" |
82 | 127 | sandbox="allow-scripts allow-same-origin allow-forms allow-popups allow-storage-access-by-user-activation" |
83 | 128 | referrerpolicy="no-referrer-when-downgrade"> |
84 | 129 | </iframe> |
85 | | - <div class="preview-error" style="display: none;"> |
| 130 | + <div class="preview-error{{ if not $showFallback }} d-none{{ end }}"> |
| 131 | + {{- if $args.image }} |
| 132 | + {{ partial "assets/image.html" (dict |
| 133 | + "src" $args.image |
| 134 | + "page" page |
| 135 | + "class" "preview-fallback-image" |
| 136 | + "title" (or $args.heading.title (printf "Preview of %s" $args.url)) |
| 137 | + "loading" "lazy" |
| 138 | + ) }} |
| 139 | + {{- end }} |
86 | 140 | <div class="alert alert-warning" role="alert"> |
87 | 141 | {{ partial "assets/icon.html" (dict "icon" $previewError "spacing" false) }} |
88 | 142 | {{ T "previewError" }} <code>{{ $args.url }}</code>. |
|
95 | 149 | {{- end -}} |
96 | 150 | </div> |
97 | 151 |
|
98 | | - {{- if $controlsBelow }} |
| 152 | + {{- if and (not $showFallback) $controlsBelow }} |
99 | 153 | <div class="container-xxl"> |
100 | 154 | <div class="preview-controls controls-bottom d-flex justify-content-center mt-3"> |
101 | 155 | <div class="btn-group" role="tablist" aria-label="Device selection"> |
|
0 commit comments