Skip to content

Commit 78f2983

Browse files
committed
fix: kleine Verbesserungen und Version 1.3.1
1 parent 01a41ce commit 78f2983

12 files changed

Lines changed: 123 additions & 85 deletions

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Changelog
22

3+
## **04.06.2026 Version 1.3.1**
4+
5+
- fix: verschiedene RexStan-Befunde behoben (u. a. null-safety beim Slice-Rendering, typisierte Konstruktor-Signaturen, generische Extension-Point-Annotationen)
6+
- fix: stabileres Preview-Rendering durch defensivere Parameterbehandlung und saubere Ruecksetzung des REDAXO-Contexts bei `force_fe`
7+
- fix: Settings-Formular abgesichert (Admin-null-check) und Template-Save validiert jetzt den erforderlichen Platzhalter `BLOCK_PEEK_CONTENT`
8+
- ux: Assets werden nur noch auf `content/edit` geladen statt global im Backend
9+
- chore: interne Dateizugriffe auf REDAXO-API (`rex_file::get`) vereinheitlicht
10+
311
## **04.06.2026 Version 1.3.0**
412

513
- feat: resolve sprog wildcards (`{{ … }}`) in slice previews when the `sprog` addon is installed — parsed in the preview's clang before the iframe is built, so backend previews match the translated frontend output (sprog's own wildcard filter only runs on the frontend)

boot.php

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@
22

33
use FriendsOfRedaxo\BlockPeek\TemplateListHider;
44

5-
/** @var rex_addon_interface $addon */
6-
$addon = $this;
5+
$addon = rex_addon::get('block_peek');
76

87
if ($addon->getConfig('inactive') !== '|1|') {
9-
if (rex::isBackend() && rex::getUser()) {
10-
rex_view::addJsFile($this->getAssetsUrl('BlockPeek.js'));
11-
rex_view::addCssFile($this->getAssetsUrl('BlockPeek.css'));
8+
if (rex::isBackend() && rex::getUser() && rex_be_controller::getCurrentPage() === 'content/edit') {
9+
rex_view::addJsFile($addon->getAssetsUrl('BlockPeek.js'));
10+
rex_view::addCssFile($addon->getAssetsUrl('BlockPeek.css'));
1211
rex_extension::register('PACKAGES_INCLUDED', function () {
1312
rex_extension::register('SLICE_BE_PREVIEW', \FriendsOfRedaxo\BlockPeek\Extension::register(...), rex_extension::LATE);
1413
});

lang/de_de.lang

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ block_peek_no_permission = Du hast keine Berechtigung, diese Seite anzusehen.
1515
block_peek_template = BlockPeek Template
1616
block_peek_template_notice = <strong>Hinweis:</strong> Der Platzhalter <b>BLOCK_PEEK_CONTENT</b> ist Voraussetzung, um den eigentlichen Slice-Inhalt anzuzeigen. <br />Das Template wird als verstecktes REDAXO-Template gespeichert (Key: <code>block_peek_internal</code>). Bitte <code>REX_ARTICLE[...]</code> nicht hinzufügen, ausser alle Slices sollen gerendert werden. <br /><strong>Tailwind 4:</strong> Wenn das <code>developer</code> Addon installiert ist, wird das Template automatisch nach <code>data/addons/developer/templates/&lt;Name&gt; [&lt;id&gt;]/template.php</code> auf die Festplatte synchronisiert, sodass Tailwind die Utility-Klassen via <code>@source</code> findet.
1717
block_peek_template_saved = Template gespeichert.
18+
block_peek_template_missing_placeholder = Das Template muss den Platzhalter BLOCK_PEEK_CONTENT enthalten.
1819

1920
# Configuration — Cache
2021
block_peek_cache_fieldset = Cache

lang/en_gb.lang

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ block_peek_no_permission = You do not have permission to view this page.
1515
block_peek_template = BlockPeek Template
1616
block_peek_template_notice = <strong>Note:</strong> The placeholder <b>BLOCK_PEEK_CONTENT</b> is required to display the actual slice content. <br />The template is stored as a hidden REDAXO template (key: <code>block_peek_internal</code>). Do not add <code>REX_ARTICLE[...]</code> unless you want all slices rendered. <br /><strong>Tailwind 4:</strong> if you use the <code>developer</code> addon, the template auto-syncs to disk at <code>data/addons/developer/templates/&lt;Name&gt; [&lt;id&gt;]/template.php</code> and Tailwind can discover utility classes via <code>@source</code>.
1717
block_peek_template_saved = Template saved.
18+
block_peek_template_missing_placeholder = The template must contain the BLOCK_PEEK_CONTENT placeholder.
1819

1920
# Configuration — Cache
2021
block_peek_cache_fieldset = Cache

lib/Extension.php

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,50 @@
88

99
class Extension
1010
{
11+
/**
12+
* @param rex_extension_point<string> $ep
13+
*/
1114
public static function register(rex_extension_point $ep): void
1215
{
13-
/** @var rex_addon $addon */
14-
$addon = rex_addon::get('block_peek');
15-
$minHeight = (int) $addon->getConfig('iframe_min_height') ?: 300;
16-
$zoomFactor = (float) $addon->getConfig('iframe_zoom_factor') ?: 0.5;
17-
$sliceData = $ep->getParams();
18-
$revision = $sliceData['revision'] ?? 0;
19-
$slice = rex_article_slice::getArticleSliceById($sliceData['slice_id'], false, 0);
20-
if (!$slice) {
21-
$revision = 1;
22-
$slice = rex_article_slice::getArticleSliceById($sliceData['slice_id'], false, 1);
23-
}
24-
$updateDate = $slice->getValue('updatedate');
25-
$generator = new Generator(articleId: $sliceData['article_id'], clangId: $sliceData['clang'], sliceId: $sliceData['slice_id'], moduleId: $sliceData['module_id'], ctypeId: $sliceData['ctype'], updateDate: $updateDate, revision: $revision);
26-
$content = $generator->getContent();
27-
$html =
28-
'<div class="block-peek-wrapper" data-zoom-factor="' . $zoomFactor . '" style="--block-peek-min-height: ' . $minHeight . 'px;">
29-
<iframe data-iframe-preview data-slice-id="' . $sliceData['slice_id'] . '" scrolling="no"
30-
srcdoc="' . htmlspecialchars($content) . '" frameborder="0" class="block-peek-iframe" style="--block-peek-zoom-factor: ' . $zoomFactor . ';"></iframe>
16+
/** @var rex_addon $addon */
17+
$addon = rex_addon::get('block_peek');
18+
$minHeight = (int) $addon->getConfig('iframe_min_height') ?: 300;
19+
$zoomFactor = (float) $addon->getConfig('iframe_zoom_factor') ?: 0.5;
20+
21+
$articleId = (int) $ep->getParam('article_id', 0);
22+
$clangId = (int) $ep->getParam('clang', 0);
23+
$sliceId = (int) $ep->getParam('slice_id', 0);
24+
$revision = (int) $ep->getParam('revision', 0);
25+
26+
if ($articleId <= 0 || $clangId <= 0 || $sliceId <= 0) {
27+
return;
28+
}
29+
30+
$slice = rex_article_slice::getArticleSliceById($sliceId, false, 0);
31+
if ($slice === null) {
32+
$revision = 1;
33+
$slice = rex_article_slice::getArticleSliceById($sliceId, false, 1);
34+
}
35+
36+
$updateDateValue = $slice?->getValue('updatedate') ?? 0;
37+
$updateDate = is_numeric($updateDateValue)
38+
? (int) $updateDateValue
39+
: (int) strtotime((string) $updateDateValue);
40+
41+
$generator = new Generator(
42+
articleId: $articleId,
43+
clangId: $clangId,
44+
sliceId: $sliceId,
45+
updateDate: $updateDate,
46+
revision: $revision,
47+
);
48+
49+
$content = $generator->getContent();
50+
$html =
51+
'<div class="block-peek-wrapper" data-zoom-factor="' . $zoomFactor . '" style="--block-peek-min-height: ' . $minHeight . 'px;">
52+
<iframe data-iframe-preview data-slice-id="' . $sliceId . '" scrolling="no"
53+
srcdoc="' . htmlspecialchars($content, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') . '" frameborder="0" class="block-peek-iframe" style="--block-peek-zoom-factor: ' . $zoomFactor . ';"></iframe>
3154
</div>';
32-
$ep->setSubject($html);
55+
$ep->setSubject($html);
3356
}
3457
}

lib/Generator.php

Lines changed: 36 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -23,23 +23,19 @@ class Generator
2323
private int $articleId = 0;
2424
private int $clangId = 0;
2525
private int $sliceId = 0;
26-
private int $moduleId = 0;
27-
private int $ctypeId = 0;
2826
private int $updateDate = 0;
2927
private int $revision = 0;
3028

3129
protected int $DEFAULT_TTL;
3230
public bool $cacheActive = true;
3331

34-
public function __construct($articleId, $clangId, $sliceId, $ctypeId, $moduleId, $updateDate, $revision)
32+
public function __construct(int $articleId, int $clangId, int $sliceId, int $updateDate, int $revision)
3533
{
3634
$this->addon = rex_addon::get('block_peek');
3735

3836
$this->articleId = $articleId;
3937
$this->clangId = $clangId;
4038
$this->sliceId = $sliceId;
41-
$this->ctypeId = $ctypeId;
42-
$this->moduleId = $moduleId;
4339
$this->updateDate = $updateDate;
4440
$this->revision = $revision;
4541

@@ -104,41 +100,46 @@ private function fetchTemplateUpdateDate(int $templateId): int
104100
private function prepareOutput(int $templateId): string
105101
{
106102
$forceFeContext = (bool) $this->addon->getConfig('force_fe', false);
103+
$wasBackendContext = rex::getProperty('redaxo');
107104
if ($forceFeContext) {
108105
rex::setProperty('redaxo', false);
109106
}
110107

111-
$context = new rex_article_content($this->articleId, $this->clangId);
112-
$context->setSliceRevision($this->revision);
113-
$context->setTemplateId($templateId);
114-
115-
$wrapperHtml = $context->getArticleTemplate();
116-
$sliceHtml = $context->getSlice($this->sliceId);
117-
118-
$sliceHtml = '<div class="block-peek-content">' . $sliceHtml . '</div>';
119-
$html = str_replace('BLOCK_PEEK_CONTENT', $sliceHtml, $wrapperHtml);
120-
121-
$html = $this->injectPosterAndStyles($html);
122-
$html = $this->setHtmlLang($html);
123-
124-
// Resolve sprog wildcards ({{ … }}) for the preview's language. sprog only
125-
// runs its OUTPUT_FILTER on the frontend (sprog/boot.php: if (!rex::isBackend())),
126-
// so without this the backend preview shows raw wildcards. Pass the preview's
127-
// clang explicitly — Wildcard::parse() defaults to rex_clang::getCurrentId(),
128-
// which in the backend is the admin's clang, not necessarily the one previewed.
129-
if (rex_addon::get('sprog')->isAvailable()) {
130-
$html = \Sprog\Wildcard::parse($html, $this->clangId);
108+
try {
109+
$context = new rex_article_content($this->articleId, $this->clangId);
110+
$context->setSliceRevision($this->revision);
111+
$context->setTemplateId($templateId);
112+
113+
$wrapperHtml = $context->getArticleTemplate();
114+
$sliceHtml = $context->getSlice($this->sliceId);
115+
116+
$sliceHtml = '<div class="block-peek-content">' . $sliceHtml . '</div>';
117+
$html = str_replace('BLOCK_PEEK_CONTENT', $sliceHtml, $wrapperHtml);
118+
119+
$html = $this->injectPosterAndStyles($html);
120+
$html = $this->setHtmlLang($html);
121+
122+
// Resolve sprog wildcards ({{ … }}) for the preview's language. sprog only
123+
// runs its OUTPUT_FILTER on the frontend (sprog/boot.php: if (!rex::isBackend())),
124+
// so without this the backend preview shows raw wildcards. Pass the preview's
125+
// clang explicitly — Wildcard::parse() defaults to rex_clang::getCurrentId(),
126+
// which in the backend is the admin's clang, not necessarily the one previewed.
127+
if (rex_addon::get('sprog')->isAvailable()) {
128+
$html = \Sprog\Wildcard::parse($html, $this->clangId);
129+
}
130+
131+
return rex_extension::registerPoint(new rex_extension_point('BLOCK_PEEK_OUTPUT', $html, [
132+
'article_id' => $this->articleId,
133+
'clang' => $this->clangId,
134+
'slice_id' => $this->sliceId,
135+
'updateDate' => $this->updateDate,
136+
'revision' => $this->revision,
137+
]));
138+
} finally {
139+
if ($forceFeContext) {
140+
rex::setProperty('redaxo', $wasBackendContext);
141+
}
131142
}
132-
133-
$html = rex_extension::registerPoint(new rex_extension_point('BLOCK_PEEK_OUTPUT', $html, [
134-
'article_id' => $this->articleId,
135-
'clang' => $this->clangId,
136-
'slice_id' => $this->sliceId,
137-
'updateDate' => $this->updateDate,
138-
'revision' => $this->revision,
139-
]));
140-
141-
return $html;
142143
}
143144

144145
private function injectPosterAndStyles(string $html): string

lib/TemplateInstaller.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use rex_config;
88
use rex_extension;
99
use rex_extension_point;
10+
use rex_file;
1011
use rex_sql;
1112
use rex_template;
1213
use rex_template_cache;
@@ -106,8 +107,8 @@ private static function buildContent(): string
106107
private static function loadDefaultContent(): string
107108
{
108109
$path = rex_addon::get('block_peek')->getPath('templates/default.php');
109-
$content = @file_get_contents($path);
110-
if ($content === false) {
110+
$content = rex_file::get($path);
111+
if (!is_string($content) || $content === '') {
111112
return "<!DOCTYPE html>\n<html>\n<head>\n<meta charset=\"UTF-8\">\n</head>\n<body>\nBLOCK_PEEK_CONTENT\n</body>\n</html>\n";
112113
}
113114
return $content;

lib/TemplateListHider.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ class TemplateListHider
1414
* OUTPUT_FILTER (LATE) on the templates list page only. Strips the <tr> for our
1515
* hidden row by matching template_id=<id> with a non-digit boundary so id "1"
1616
* doesn't accidentally match "10".
17+
*
18+
* @param rex_extension_point<string> $ep
1719
*/
1820
public static function register(rex_extension_point $ep): void
1921
{
@@ -34,9 +36,6 @@ public static function register(rex_extension_point $ep): void
3436
$hideId = $template->getId();
3537

3638
$content = $ep->getSubject();
37-
if (!is_string($content)) {
38-
return;
39-
}
4039

4140
$result = preg_replace_callback(
4241
'/<tr\b[^>]*>.*?<\/tr>/s',

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "block_peek-assets",
3-
"version": "1.3.0",
3+
"version": "1.3.1",
44
"description": "Asset build for BlockPeek Addon",
55
"scripts": {
66
"version:sync": "node scripts/sync-version.js",

0 commit comments

Comments
 (0)