Upload PDFs to the library, link them from any link target, show them in a viewer
PDFs go through the same upload field as images, are checked by their content and kept unchanged in their own table. Every link field can pick one, and a page that links a PDF opens it in a dialog with the browser's own viewer; touch devices and browsers without a viewer get a new tab instead. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
04927b9563
commit
e8a9dcdf5c
20 files changed
+667
-30
No files matched your search
@@ -130,6 +130,10 @@ final class PageController extends AbstractController
|
||||
// Stylesheet und Skript eines Plugin-Bausteins kommen nur dorthin, wo er vorkommt.
|
||||
$pluginAssets = $this->plugins->assetsFor($documents);
|
||||
$needsZoom = (bool) array_filter($documents, fn (array $doc): bool => $this->hasZoom($doc));
|
||||
// Ein Link auf ein PDF der Mediathek - in einem Button, einem Bild oder im Fliesstext.
|
||||
// Gesucht wird im gespeicherten Dokument als Text: so findet sich auch ein Link, den
|
||||
// jemand in einen formatierten Absatz geschrieben hat.
|
||||
$needsDocument = (bool) array_filter($documents, static fn (array $doc): bool => 1 === preg_match('#/media/[a-f0-9]{32}\.pdf#', json_encode($doc, JSON_UNESCAPED_SLASHES) ?: ''));
|
||||
// Only the header changes with the scroll position; a scrolled value anywhere in it asks
|
||||
// for the small script that marks the page as scrolled.
|
||||
// Den Nach-oben-Knopf blendet dasselbe Skript ein; ohne es bleibt er dauerhaft sichtbar.
|
||||
@@ -198,6 +202,9 @@ final class PageController extends AbstractController
|
||||
// Ein Bild, das sich vergroessern laesst, ist ohne Skript ein Verweis auf die
|
||||
// grosse Fassung. Das Skript kommt nur dazu, wo es etwas zu tun hat.
|
||||
'needsZoom' => $needsZoom,
|
||||
// Der PDF-Betrachter, nach demselben Muster und im selben Skript: ohne es ein
|
||||
// gewoehnlicher Link.
|
||||
'needsDocument' => $needsDocument,
|
||||
// Own scripts arrive as one file per page, never inline, so the policy can stay
|
||||
// hash based and no page without a script loses its strict setting.
|
||||
'pageScript' => [] === $this->code->scriptsOf($page['document']) ? null : '/assets/seite/'.$page['id'].'.js',
|
||||
@@ -228,7 +235,7 @@ final class PageController extends AbstractController
|
||||
// Die Pruefung braucht ein Skript von Cloudflare und einen Rahmen dafuer. Beides wird nur
|
||||
// dort erlaubt, wo tatsaechlich ein Formular steht - nicht auf jeder Seite der Website.
|
||||
$mitTurnstile = $this->turnstile->isActive() && (bool) array_filter($documents, fn (array $doc): bool => $this->hasType($doc, 'form'));
|
||||
$wantsScript = ($mitTurnstile || $needsTabs || $needsFormScript || [] !== $pluginAssets['scripts'] || [] !== $areaScripts || $needsSlider || $needsMotion || $needsScrolled || $needsZoom || [] !== $this->code->scriptsOf($page['document']));
|
||||
$wantsScript = ($mitTurnstile || $needsTabs || $needsFormScript || [] !== $pluginAssets['scripts'] || [] !== $areaScripts || $needsSlider || $needsMotion || $needsScrolled || $needsZoom || $needsDocument || [] !== $this->code->scriptsOf($page['document']));
|
||||
$frames = $this->frameHosts($request, $page['document'], ...array_values($areas), ...array_values($sections));
|
||||
if ($mitTurnstile) {
|
||||
$frames[] = 'challenges.cloudflare.com';
|
||||
@@ -238,7 +245,7 @@ final class PageController extends AbstractController
|
||||
$response->headers->set('X-Robots-Tag', 'noindex, follow');
|
||||
}
|
||||
|
||||
return $this->shell($response, $wantsScript, $styles, $frames);
|
||||
return $this->shell($response, $wantsScript, $styles, $frames, $needsDocument);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -252,8 +259,9 @@ final class PageController extends AbstractController
|
||||
* @param list<string> $frameHosts Hosts of embeds that are actually rendered on this response.
|
||||
* Nothing is added to the policy for an embed still waiting
|
||||
* for consent.
|
||||
* @param bool $frameSelf whether the page may frame its own PDFs (the viewer)
|
||||
*/
|
||||
private function shell(Response $response, bool $allowScript = false, string $styles = '', array $frameHosts = []): Response
|
||||
private function shell(Response $response, bool $allowScript = false, string $styles = '', array $frameHosts = [], bool $frameSelf = false): Response
|
||||
{
|
||||
$styleSources = ["'self'"];
|
||||
if ('' !== $styles) {
|
||||
@@ -280,7 +288,9 @@ final class PageController extends AbstractController
|
||||
implode(' ', $scriptSources),
|
||||
implode(' ', $styleSources),
|
||||
implode(' ', $fontSources),
|
||||
[] === $frameHosts ? "'none'" : implode(' ', array_map(static fn (string $host): string => 'https://'.$host, $frameHosts)),
|
||||
// Der PDF-Betrachter zeigt eine Datei dieser Website in einem Rahmen - nur dort, wo
|
||||
// eine Seite auf ein PDF verlinkt, und nur von hier.
|
||||
[] === $frameHosts && !$frameSelf ? "'none'" : implode(' ', [...($frameSelf ? ["'self'"] : []), ...array_map(static fn (string $host): string => 'https://'.$host, $frameHosts)]),
|
||||
));
|
||||
|
||||
return $response;
|
||||
|
||||
Reference in new issue
Block a user