Skip to content

feat(!!!): add TYPO3 v14 support and replace static templates with native content elements#31

Merged
konradmichalik merged 29 commits intomainfrom
add-support-typo3-14
Mar 5, 2026
Merged

feat(!!!): add TYPO3 v14 support and replace static templates with native content elements#31
konradmichalik merged 29 commits intomainfrom
add-support-typo3-14

Conversation

@konradmichalik
Copy link
Contributor

@konradmichalik konradmichalik commented Mar 5, 2026

Fixes #29 & #30

Summary

  • Replace bw-static-template dependency with native IRRE content elements (Colors, Fonts, Icons, Images)
  • Add TYPO3 v14 / Fluid v4 compatibility across all ViewHelpers and backend components
  • Add rich backend preview renderer for all styleguide content elements
  • Add dedicated Styleguide wizard tab with page type restriction
  • Add custom content element icons
  • Redesign extension and page type icons
  • Update documentation to reflect the new architecture

Breaking Changes

  • bw-static-template is no longer a hard dependency (moved to suggest)
  • The old static template patterns (Patterns/Colors.html, Patterns/Templates.html, etc.) are replaced by dedicated content elements with IRRE child records
  • Render\TemplateViewHelper has been removed
  • New database tables: tx_metypo3styleguide_color, tx_metypo3styleguide_font, tx_metypo3styleguide_image

Changes

  • Classes/Preview/StyleguidePreviewRenderer.php — New backend preview with color swatches, font specimens, icon/image thumbnails
  • Classes/ViewHelpers/*.php — Converted from renderStatic() to render() for Fluid v4 compatibility
  • Classes/ViewHelpers/Uri/ResourcePathViewHelper.php — New ViewHelper for resolving EXT: paths
  • Configuration/TCA/tx_metypo3styleguide_*.php — New TCA for color, font, image child records
  • Configuration/TCA/Overrides/tt_content.php — Register 4 new CTypes with IRRE fields and custom icons
  • Configuration/TsConfig/ContentElements/*.tsconfig — Dedicated Styleguide wizard tab
  • Configuration/TsConfig/TCEFORM.tsconfig — Restrict styleguide CEs to styleguide page type
  • Resources/Private/Templates/*.html — Frontend templates for new content elements
  • Resources/Public/Icons/content-styleguide-*.svg — Custom content element icons
  • composer.json — Add TYPO3 v14, Fluid v4/v5 support; move bw-static-template to suggest

Summary by CodeRabbit

  • New Features

    • Four new Styleguide content elements (Colors, Fonts, Icons, Images) with backend previews, frontend templates, wizard entries and icons; enhanced Technical Headline preview.
  • Changed

    • Added TYPO3 v14 support and raised PHP requirement to 8.2+; installer/setup updated to better handle multiple TYPO3 versions; static-template is now optional/suggested.
  • Documentation

    • Expanded Content Elements docs and README; removed outdated pattern and static-template docs.

@coderabbitai
Copy link

coderabbitai bot commented Mar 5, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds TYPO3 v14 support and new styleguide content elements (Colors, Fonts, Icons, Images): introduces TCA and DB schema, a backend preview renderer, Fluid templates and ViewHelper signature updates, removes TemplateViewHelper/bw_static_template usage, updates DDEV scripts and documentation, and adjusts fixtures and composer metadata.

Changes

Cohort / File(s) Summary
DDEV / setup scripts
\.ddev/.setup/scripts/utils.sh, \.ddev/commands/web/.install-14, \.ddev/commands/web/.install-12, \.ddev/commands/web/.install-13, \.ddev/commands/web/.install-11, \.ddev/docker-compose.typo3-setup.yaml, \.ddev/config.typo3-setup.yaml
Adds TYPO3 14 to environment list, makes TYPO3_BIN version-aware, adds post_setup_11, stubs/simplifies per-version install scripts and adjusts DDEV hostnames.
Composer / metadata / CI
composer.json, ext_emconf.php, .github/workflows/release.yml, composer-unused.php, README.md
Extends composer constraints to include TYPO3 v14, moves blueways/bw-static-template to suggests, adds typo3/cms-backend, updates ext_emconf suggests, tweaks reusable workflow reference and README supported versions.
New content elements & DB schema
Configuration/TCA/Overrides/tt_content.php, ext_tables.sql, Configuration/TCA/tx_metypo3styleguide_color.php, Configuration/TCA/tx_metypo3styleguide_font.php, Configuration/TCA/tx_metypo3styleguide_image.php, Tests/Acceptance/Fixtures/data.xml
Adds new CType entries and tt_content columns, three new tx_metypo3styleguide_* tables with TCA files, updates ext_tables.sql and large fixture changes replacing bw_static_template data.
Backend preview renderer
Classes/Preview/StyleguidePreviewRenderer.php
New preview renderer dispatching by CType to build backend preview HTML from child records or filesystem/icon paths; adds Fluid rendering helper with v12/v13+ compatibility handling.
ViewHelpers modernized
Classes/ViewHelpers/ContrastColorViewHelper.php, Classes/ViewHelpers/FilesViewHelper.php, Classes/ViewHelpers/Format/FilenameViewHelper.php
Converted several ViewHelpers from static renderStatic(...) signatures to instance render() methods and switched argument access to $this->arguments.
New ViewHelper & templates
Classes/ViewHelpers/Uri/ResourcePathViewHelper.php, Resources/Private/Templates/Colors.html, Resources/Private/Templates/Fonts.html, Resources/Private/Templates/Icons.html, Resources/Private/Templates/Images.html, Resources/Private/Templates/Preview/*
Adds ResourcePathViewHelper, new frontend and backend preview templates for Colors/Fonts/Icons/Images, and updates templates to use nested data paths and the new resource helper.
Removed TemplateViewHelper & docs
Classes/ViewHelpers/Render/TemplateViewHelper.php, Documentation/Patterns.md, Documentation/StaticTemplates.md, Documentation/ViewHelpers/Classes/TemplateViewHelper.md
Removes TemplateViewHelper and associated documentation that enabled bw_static_template-driven arbitrary template rendering.
TSConfig / TypoScript / icons / language
Configuration/TsConfig/ContentElements/*, Configuration/TsConfig/TCEFORM.tsconfig, Configuration/TypoScript/setup.typoscript, Configuration/TypoScript/constants.typoscript, Configuration/Icons.php, Resources/Private/Language/locallang.xlf, Resources/Private/Language/de.locallang.xlf
Registers new wizard items and icons, adds TCEFORM visibility rules, integrates TypoScript for new elements (dataProcessing), removes bw_static_template imports, and adds localization strings.
Documentation & README
Documentation/ContentElement.md, README.md
Expands content element documentation for new elements and updates README (supported TYPO3/PHP versions, features).
Tests & fixtures
Tests/Acceptance/Fixtures/data.xml
Large fixture updates: new table records for colors/fonts/images, updated CType values, removal of legacy bw_static_template fields, and other test data migrations.

Sequence Diagram(s)

sequenceDiagram
    participant PageModule as Backend Page Module
    participant Preview as StyleguidePreviewRenderer
    participant DB as TYPO3 Database
    participant FS as File System / PathUtility
    participant Template as Fluid Template Renderer

    PageModule->>Preview: renderPageModulePreviewContent(GridColumnItem)
    Preview->>Preview: inspect CType

    alt colors/fonts/images
        Preview->>DB: query tx_metypo3styleguide_* child records
        DB-->>Preview: return records
        Preview->>Template: render preview template (Preview/*)
        Template-->>Preview: HTML
    else icons
        Preview->>FS: resolve icons directory path & list files
        FS-->>Preview: list files (capped)
        Preview->>Template: render icons preview template
        Template-->>Preview: HTML
    end

    Preview-->>PageModule: return preview HTML
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~65 minutes

Possibly related issues

  • Add TYPO3 v14 support #30: TYPO3 v14 compatibility — this PR updates composer constraints, DDEV config, and README to include v14, addressing that objective.
  • Add TYPO3 v14 support #30 (vector match): changes to DDEV scripts and version-aware logic align with the same compatibility goal.

Possibly related PRs

Suggested reviewers

  • jackd248

Poem

🐇 I hopped through code with nimble paws,
added swatches, fonts and icon laws.
TYPO3 fourteen joins the scene,
previews bloom with tidy sheen.
A little rabbit cheers — styleguide dreams!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.87% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: adding TYPO3 v14 support and replacing static templates with native content elements, which matches the substantial refactoring throughout the changeset.
Linked Issues check ✅ Passed All objectives from issue #29 (extend composer constraints for TYPO3 v14, move bw-static-template to suggest, update ViewHelpers for Fluid v4, adapt TCA/TSconfig/templates) are fully implemented and verified in the changeset.
Out of Scope Changes check ✅ Passed The PR includes a comprehensive refactoring that stays within scope: DDEV setup updates, ViewHelper migrations, new preview renderer, TCA restructuring, fixture updates, and documentation changes all directly support TYPO3 v14 compatibility and the static-template replacement initiative.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch add-support-typo3-14

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
ext_emconf.php (1)

23-31: ⚠️ Potential issue | 🟠 Major

Update ext_emconf.php TYPO3 constraint to include v14 support.

The ext_emconf.php constraint on line 26 caps TYPO3 at 13.4.99, conflicting with the stated support for TYPO3 14 declared in composer.json (^12.0 || ^13.0 || ^14.0). This mismatch will prevent installation via Extension Manager on TYPO3 14 despite the extension being compatible.

Proposed fix
-            'typo3' => '12.4.0-13.4.99',
+            'typo3' => '12.4.0-14.4.99',
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ext_emconf.php` around lines 23 - 31, The TYPO3 version constraint in
ext_emconf.php under the 'constraints' -> 'depends' -> 'typo3' entry currently
limits versions to '12.4.0-13.4.99'; update that constraint to include TYPO3 v14
by expanding the upper bound (for example to '12.4.0-14.99.99' or similar) so it
matches composer.json's supported ranges (^12.0 || ^13.0 || ^14.0) and allows
installation on TYPO3 14 via the Extension Manager.
Classes/ViewHelpers/FilesViewHelper.php (1)

44-48: ⚠️ Potential issue | 🟠 Major

Handle scandir() failure explicitly.

Line 44 assumes scandir($path) always returns an array. Although the code validates is_dir($path) at line 39, this is insufficient; scandir() can still return false due to permission issues or race conditions (directory becomes unreadable between the is_dir() check and scandir() call). This results in a TypeError when the foreach tries to iterate over false. Similar code in Classes/Preview/StyleguidePreviewRenderer.php correctly handles this with an explicit false check.

Add an explicit check for scandir() failure:

Proposed fix
+        $entries = scandir($path);
+        if ($entries === false) {
+            throw new InvalidArgumentException('The provided path is not readable: '.$path, 4247501750);
+        }
+
         $files = [];
-        foreach (scandir($path) as $file) {
+        foreach ($entries as $file) {
             if ('.' !== $file && '..' !== $file && is_file($path.'/'.$file)) {
                 $files[] = $file;
             }
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Classes/ViewHelpers/FilesViewHelper.php` around lines 44 - 48, The foreach in
FilesViewHelper.php assumes scandir($path) returns an array and will TypeError
on false; update the code that calls scandir($path) (inside the FilesViewHelper
class/method that currently checks is_dir($path)) to store the result in a
variable, check it !== false before iterating, and handle the false case (e.g.,
treat as empty array or log/return early) similar to the handling in
Classes/Preview/StyleguidePreviewRenderer.php so the loop only runs on a valid
array from scandir.
Classes/ViewHelpers/ContrastColorViewHelper.php (1)

37-43: ⚠️ Potential issue | 🟡 Minor

Length-only validation accepts invalid HEX color strings.

Line 37 only checks length, so values like GGGGGG pass and compute as unintended RGB values.

✅ Proposed validation tightening
         $hexColor = ltrim((string) $this->arguments['color'], '#');
-        if (6 !== strlen($hexColor)) {
+        if (!preg_match('/^[0-9A-Fa-f]{6}$/', $hexColor)) {
             throw new InvalidArgumentException('Invalid HEX color code: '.$this->arguments['color'], 4518636088);
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Classes/ViewHelpers/ContrastColorViewHelper.php` around lines 37 - 43, The
length-only check for $hexColor in ContrastColorViewHelper accepts non-hex
characters (e.g., "GGGGGG"); update validation to ensure $hexColor contains only
valid hex digits (0-9, a-f, A-F) after stripping an optional leading '#' before
proceeding to hexdec conversions, and if invalid throw the same
InvalidArgumentException (use the existing message/exception type) so only
proper HEX strings reach the substr/hexdec logic.
🧹 Nitpick comments (5)
Classes/ViewHelpers/Format/FilenameViewHelper.php (1)

26-33: filename argument is registered but ignored in render().

Line 28 declares filename, but Line 33 always reads children. Self-closing usage with filename="..." won’t work as expected.

♻️ Proposed adjustment
 public function render(): string
 {
-    $value = $this->renderChildren();
+    $value = $this->arguments['filename'] ?? $this->renderChildren();

     return pathinfo((string) $value, \PATHINFO_FILENAME);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Classes/ViewHelpers/Format/FilenameViewHelper.php` around lines 26 - 33, The
registered 'filename' argument is never used in render(), so update the render()
method in FilenameViewHelper to prefer the passed argument (from
$this->arguments['filename'] or $this->getArgument('filename')) and only call
$this->renderChildren() as a fallback when the argument is null/empty; ensure
you keep the existing formatting logic that consumes the computed $value so both
self-closing usage (filename="...") and tag content work the same.
.ddev/commands/web/.install-13 (1)

10-13: Consider removing the commented placeholder composer block.

These commented lines look like unfinished setup logic and add noise to operational scripts.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.ddev/commands/web/.install-13 around lines 10 - 13, Remove the leftover
commented placeholder composer block (the lines beginning with "#_progress", "# 
composer req x/y:'^1.0' \", and "#_done") so the script has no noisy unused
comments; simply delete those commented lines from the install script (or
replace them with an explanatory single-line comment if you want a note),
ensuring no functional change to the surrounding script.
Configuration/TypoScript/setup.typoscript (1)

13-20: Optional: extract shared query-processor config to reduce drift.

The three DatabaseQueryProcessor blocks duplicate where, marker, and sorting setup. A shared base object would simplify maintenance.

♻️ Possible TypoScript refactor
+lib.metypo3styleguideIrreProcessor = TYPO3\CMS\Frontend\DataProcessing\DatabaseQueryProcessor
+lib.metypo3styleguideIrreProcessor {
+  where = parentid=###uid### AND parenttable='tt_content'
+  markers.uid.field = uid
+  orderBy = sorting
+}
+
 tt_content {
   metypo3styleguide_colors {
-    dataProcessing.10 = TYPO3\CMS\Frontend\DataProcessing\DatabaseQueryProcessor
-    dataProcessing.10 {
-      table = tx_metypo3styleguide_color
-      where = parentid=###uid### AND parenttable='tt_content'
-      markers.uid.field = uid
-      orderBy = sorting
-      as = colors
-    }
+    dataProcessing.10 < lib.metypo3styleguideIrreProcessor
+    dataProcessing.10.table = tx_metypo3styleguide_color
+    dataProcessing.10.as = colors
   }
   metypo3styleguide_fonts {
-    dataProcessing.10 = TYPO3\CMS\Frontend\DataProcessing\DatabaseQueryProcessor
-    dataProcessing.10 {
-      table = tx_metypo3styleguide_font
-      where = parentid=###uid### AND parenttable='tt_content'
-      markers.uid.field = uid
-      orderBy = sorting
-      as = fonts
-    }
+    dataProcessing.10 < lib.metypo3styleguideIrreProcessor
+    dataProcessing.10.table = tx_metypo3styleguide_font
+    dataProcessing.10.as = fonts
   }
   metypo3styleguide_images {
-    dataProcessing.10 = TYPO3\CMS\Frontend\DataProcessing\DatabaseQueryProcessor
-    dataProcessing.10 {
-      table = tx_metypo3styleguide_image
-      where = parentid=###uid### AND parenttable='tt_content'
-      markers.uid.field = uid
-      orderBy = sorting
-      as = images
-    }
+    dataProcessing.10 < lib.metypo3styleguideIrreProcessor
+    dataProcessing.10.table = tx_metypo3styleguide_image
+    dataProcessing.10.as = images
   }
 }

Also applies to: 26-33, 44-51

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Configuration/TypoScript/setup.typoscript` around lines 13 - 20, The three
DatabaseQueryProcessor blocks (e.g., dataProcessing.10 using
TYPO3\CMS\Frontend\DataProcessing\DatabaseQueryProcessor with table, where,
markers.uid.field and orderBy = sorting) duplicate the same where/marker/sorting
settings; extract those shared settings into a single reusable TypoScript object
(e.g., a base processor object) and then include or reference that base from
each dataProcessing entry so each block only overrides the unique pieces like
table and as, reducing drift and keeping where/markers/orderBy consistent across
dataProcessing.10, dataProcessing.26 and dataProcessing.44.
Configuration/TCA/Overrides/tt_content.php (1)

218-292: Consider extracting shared type config to reduce showitem duplication.

Lines 218–292 repeat nearly identical layout blocks for four types. A small shared base array/string would reduce drift risk in future updates.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Configuration/TCA/Overrides/tt_content.php` around lines 218 - 292, Extract
the duplicated 'showitem' definition into a single shared variable (e.g.
$styleguideShowItem or $styleguideShowItemBase) and reuse it for each type
('metypo3styleguide_colors', 'metypo3styleguide_fonts',
'metypo3styleguide_icons', 'metypo3styleguide_images') by appending only the
type‑specific field (tx_metypo3styleguide_colors, tx_metypo3styleguide_fonts,
tx_metypo3styleguide_icons_path, tx_metypo3styleguide_images) to the base
string/array; keep the 'previewRenderer' => StyleguidePreviewRenderer::class
assignment for each type and replace the inline duplicated 'showitem' blocks
with the shared variable to avoid duplication and drift.
Classes/Preview/StyleguidePreviewRenderer.php (1)

135-159: Filter icon directory to image files only and handle missing web paths.

Line 135 uses scandir() to load all files without extension filtering. In directories containing mixed file types, this forces thumbnail attempts on non-image files, causing broken image requests and unnecessary processing.

Replace scandir() with FilesystemIterator and filter for image extensions. Additionally, check that PathUtility::getAbsoluteWebPath() returns a non-empty string before rendering the image tag, consistent with the pattern used in renderImagesPreview().

♻️ Proposed refactor
-        $files = scandir($absDir);
-        if (false === $files) {
-            return '<span style="font-size:11px;color:`#666`;">'.htmlspecialchars($path, ENT_QUOTES).'</span>';
-        }
-
         $items = '';
         $count = 0;
-        foreach ($files as $file) {
-            if ('.' === $file || '..' === $file || !is_file($absDir.'/'.$file)) {
+        $iterator = new \FilesystemIterator($absDir, \FilesystemIterator::SKIP_DOTS);
+        $allowed = ['svg', 'png', 'jpg', 'jpeg', 'gif', 'webp'];
+        foreach ($iterator as $entry) {
+            if (!$entry->isFile()) {
                 continue;
             }
+            $extension = strtolower($entry->getExtension());
+            if (!in_array($extension, $allowed, true)) {
+                continue;
+            }

             ++$count;
             if ($count > 20) {
                 $items .= '<div style="display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;font-size:11px;color:`#666`;">…</div>';

                 break;
             }

-            $webPath = PathUtility::getAbsoluteWebPath($absDir.'/'.$file);
-            $name = htmlspecialchars(pathinfo($file, \PATHINFO_FILENAME), ENT_QUOTES);
+            $filePath = $entry->getPathname();
+            $webPath = PathUtility::getAbsoluteWebPath($filePath);
+            if ('' === $webPath) {
+                continue;
+            }
+            $name = htmlspecialchars(pathinfo($entry->getFilename(), \PATHINFO_FILENAME), ENT_QUOTES);
             $items .= '<div style="display:inline-block;margin:0 4px 4px 0;text-align:center;" title="'.$name.'">'
                 .'<img src="'.htmlspecialchars($webPath, ENT_QUOTES).'" alt="'.$name.'" style="width:32px;height:32px;object-fit:contain;" loading="lazy" />'
                 .'</div>';
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Classes/Preview/StyleguidePreviewRenderer.php` around lines 135 - 159,
Replace the raw scandir() loop with a FilesystemIterator over $absDir and only
iterate files whose extensions match allowed image types (e.g.
jpg,jpeg,png,gif,webp,svg), preserving the existing $count/limit and thumbnail
markup logic; for each candidate, call
PathUtility::getAbsoluteWebPath($absDir.'/'.$file) and skip rendering if it
returns an empty string (mirroring renderImagesPreview()'s pattern) so no <img>
tags are emitted for non-image files or missing web paths, and keep using
htmlspecialchars for $name and the existing loading="lazy"/thumb sizing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.ddev/.setup/scripts/utils.sh:
- Around line 33-34: The script commented out the initial file-descriptor setup
(the two exec lines) but _done() still attempts to restore them with exec 1>&3
2>&4, causing "bad file descriptor" in spinner mode; fix by either restoring the
original exec lines (re-enable the exec 3>&1 4>&2 and exec >/dev/null 2>&1 that
open fd 3 and 4) or modify _done() to only restore if fds 3 and 4 are open
(check /proc/self/fd or use `exec 3>&-` guards) so the exec 1>&3 2>&4 call is
never invoked when those descriptors were not created.

In @.ddev/commands/web/.install-12:
- Line 5: The top comment stating "Pre-setup function for TYPO3 version 13" is
incorrect relative to the call to pre_setup 12; update the comment to match the
actual target version or change the invoked helper to match the
comment—specifically either change the comment text to "TYPO3 version 12" or
replace the call to pre_setup 12 with pre_setup 13 so the file's comment and the
pre_setup invocation (pre_setup 12) are consistent; ensure the phrase
referencing the version in the comment matches the symbol pre_setup X used
below.

In @.github/workflows/release.yml:
- Line 10: Replace the mutable ref `@main` in the reusable workflow reference
`uses: jackd248/reusable-github-actions/.github/workflows/release.yml@main` with
a pinned commit SHA; obtain the exact SHA for the repository's main branch
(e.g., via GitHub API or git ls-remote) and update the uses line to
`...@<commit-sha>` so the workflow points to an immutable commit instead of
`@main`.

In `@Classes/Preview/StyleguidePreviewRenderer.php`:
- Around line 216-230: The getChildRecords method is performing a DB query even
when $parentId is 0 (unsaved/new CE); update getChildRecords(string $table, int
$parentId): array to early-return an empty array when $parentId is 0 (or
non-positive) to avoid unnecessary queries and accidental matching of orphan
rows, then only proceed to build and execute the ConnectionPool/QueryBuilder
query when $parentId > 0.

In `@Configuration/TCA/tx_metypo3styleguide_color.php`:
- Around line 19-27: The table schema for tx_metypo3styleguide_color is missing
the standard TYPO3 ctrl columns (tstamp, crdate, deleted, hidden, sorting);
update ext_tables.sql to add these columns to the CREATE TABLE for
tx_metypo3styleguide_color (include tstamp int(11) unsigned, crdate int(11)
unsigned, deleted tinyint(4) unsigned, hidden tinyint(4), sorting int(11) plus
appropriate DEFAULT/NOT NULL definitions) and ensure PRIMARY KEY (uid) and KEY
parentid exist so the TCA fields referenced in ctrl (tstamp, crdate, deleted,
sorting, enablecolumns.disabled => hidden) align with the DB schema.
- Around line 46-52: Replace the current TCA input configuration that uses
'type' => 'input' (and the attributes 'max' => 7 and 'eval' => 'trim') with the
native color field config by setting 'config' => ['type' => 'color'] so the
field enforces `#RRGGBB` format; keep 'required' if needed and remove 'max' and
'eval' because the core color type handles trimming and length validation.
Target the array that currently contains 'type' => 'input', 'size' => 10, 'max'
=> 7, 'eval' => 'trim', 'required' => true, 'placeholder' => '#000000' and
replace it with a config using 'config' => ['type' => 'color'] (retaining or
adjusting 'placeholder'/'required' as appropriate).

In `@Configuration/TCA/tx_metypo3styleguide_font.php`:
- Around line 54-62: The TCA currently lets arbitrary text into the font_weight
field (identifier 'font_weight') which is later injected into inline CSS in
Resources/Private/Templates/Fonts.html; change the TCA config from an
unrestricted 'type' => 'input' to a constrained choice (e.g., 'type' => 'select'
with 'items' listing valid CSS font-weight tokens:
100,200,300,400,500,600,700,800,900 and optionally 'normal'/'bold'), set a
sensible default/placeholder (e.g., '400') and ensure the value saved is the
numeric/string token expected by the template; optionally add 'eval' => 'int' if
you keep an input but prefer numeric validation — update 'font_weight' config
accordingly so invalid tokens cannot be entered and thus won't break the inline
CSS in Fonts.html.

In `@Configuration/TCA/tx_metypo3styleguide_image.php`:
- Around line 46-52: The TCA for the 'path' field currently only trims input and
allows arbitrary strings; update the field definition in
tx_metypo3styleguide_image.php (the array entry for 'path') to validate EXT:
resource syntax by adding a stricter eval: either include a whitelist check
(e.g. use an is_in-style eval if you register allowed prefixes) or, preferably,
register and use a custom eval class (e.g.
Vendor\YourExt\Evaluation\ExtPathEval) and add its token to the eval string (for
example 'eval' => 'trim,required,tx_yourext_extpatheval') so only valid
"EXT:extensionKey/..." paths are persisted and passed to
GeneralUtility::getFileAbsFileName().

In `@ext_tables.sql`:
- Around line 10-33: The child tables tx_metypo3styleguide_color,
tx_metypo3styleguide_font and tx_metypo3styleguide_image must define the sorting
column referenced by your inline configs; update each CREATE TABLE
(tx_metypo3styleguide_color, tx_metypo3styleguide_font,
tx_metypo3styleguide_image) to add a sorting int(11) DEFAULT 0 NOT NULL column
and remove any trailing commas in the column list so the SQL is valid; this
ensures foreign_sortby => 'sorting' works when TYPO3 persists/reorders inline
children.

In `@README.md`:
- Line 8: The badge markdown has an empty link destination "[![Supported TYPO3
versions](https://badgen.net/badge/TYPO3/12%20&%2013%20&%2014/orange)]()", which
makes the badge non-clickable; update that markdown to include a valid URL
target (for example the project homepage, release/tags page, or the TYPO3
compatibility docs) by replacing the empty parentheses with the intended
destination so the badge becomes a functional link.

In `@Resources/Private/Templates/Colors.html`:
- Around line 15-17: The template currently calls sg:contrastColor with
unvalidated editor input (variable contrastColor using {color.data.color}),
which can throw in Classes/ViewHelpers/ContrastColorViewHelper.php for malformed
HEX values; update the Colors.html template to validate or sanitize
{color.data.color} before calling sg:contrastColor and wrap the helper
invocation in a fail-soft fallback: check the string format (e.g., regex for
`#RRGGBB` or `#RGB`) and only call sg:contrastColor when valid, otherwise set
contrastColor to a safe default (e.g., "#000000" or computed default) so
rendering never throws; ensure the change references the contrastColor variable
and uses the same sg:contrastColor helper name.

In `@Resources/Private/Templates/Icons.html`:
- Line 17: Normalize data.tx_metypo3styleguide_icons_path before concatenation
so it always has exactly one trailing slash; update the template that builds the
src (the <img> using {sg:uri.resourcePath(path:
'{data.tx_metypo3styleguide_icons_path}{file}')} ) to trim any trailing slash
from data.tx_metypo3styleguide_icons_path and then append a single "/" before
{file} (or use a ViewHelper that joins path segments), ensuring the resourcePath
always receives a valid path with one separator between base path and file.

---

Outside diff comments:
In `@Classes/ViewHelpers/ContrastColorViewHelper.php`:
- Around line 37-43: The length-only check for $hexColor in
ContrastColorViewHelper accepts non-hex characters (e.g., "GGGGGG"); update
validation to ensure $hexColor contains only valid hex digits (0-9, a-f, A-F)
after stripping an optional leading '#' before proceeding to hexdec conversions,
and if invalid throw the same InvalidArgumentException (use the existing
message/exception type) so only proper HEX strings reach the substr/hexdec
logic.

In `@Classes/ViewHelpers/FilesViewHelper.php`:
- Around line 44-48: The foreach in FilesViewHelper.php assumes scandir($path)
returns an array and will TypeError on false; update the code that calls
scandir($path) (inside the FilesViewHelper class/method that currently checks
is_dir($path)) to store the result in a variable, check it !== false before
iterating, and handle the false case (e.g., treat as empty array or log/return
early) similar to the handling in Classes/Preview/StyleguidePreviewRenderer.php
so the loop only runs on a valid array from scandir.

In `@ext_emconf.php`:
- Around line 23-31: The TYPO3 version constraint in ext_emconf.php under the
'constraints' -> 'depends' -> 'typo3' entry currently limits versions to
'12.4.0-13.4.99'; update that constraint to include TYPO3 v14 by expanding the
upper bound (for example to '12.4.0-14.99.99' or similar) so it matches
composer.json's supported ranges (^12.0 || ^13.0 || ^14.0) and allows
installation on TYPO3 14 via the Extension Manager.

---

Nitpick comments:
In @.ddev/commands/web/.install-13:
- Around line 10-13: Remove the leftover commented placeholder composer block
(the lines beginning with "#_progress", "#  composer req x/y:'^1.0' \", and
"#_done") so the script has no noisy unused comments; simply delete those
commented lines from the install script (or replace them with an explanatory
single-line comment if you want a note), ensuring no functional change to the
surrounding script.

In `@Classes/Preview/StyleguidePreviewRenderer.php`:
- Around line 135-159: Replace the raw scandir() loop with a FilesystemIterator
over $absDir and only iterate files whose extensions match allowed image types
(e.g. jpg,jpeg,png,gif,webp,svg), preserving the existing $count/limit and
thumbnail markup logic; for each candidate, call
PathUtility::getAbsoluteWebPath($absDir.'/'.$file) and skip rendering if it
returns an empty string (mirroring renderImagesPreview()'s pattern) so no <img>
tags are emitted for non-image files or missing web paths, and keep using
htmlspecialchars for $name and the existing loading="lazy"/thumb sizing.

In `@Classes/ViewHelpers/Format/FilenameViewHelper.php`:
- Around line 26-33: The registered 'filename' argument is never used in
render(), so update the render() method in FilenameViewHelper to prefer the
passed argument (from $this->arguments['filename'] or
$this->getArgument('filename')) and only call $this->renderChildren() as a
fallback when the argument is null/empty; ensure you keep the existing
formatting logic that consumes the computed $value so both self-closing usage
(filename="...") and tag content work the same.

In `@Configuration/TCA/Overrides/tt_content.php`:
- Around line 218-292: Extract the duplicated 'showitem' definition into a
single shared variable (e.g. $styleguideShowItem or $styleguideShowItemBase) and
reuse it for each type ('metypo3styleguide_colors', 'metypo3styleguide_fonts',
'metypo3styleguide_icons', 'metypo3styleguide_images') by appending only the
type‑specific field (tx_metypo3styleguide_colors, tx_metypo3styleguide_fonts,
tx_metypo3styleguide_icons_path, tx_metypo3styleguide_images) to the base
string/array; keep the 'previewRenderer' => StyleguidePreviewRenderer::class
assignment for each type and replace the inline duplicated 'showitem' blocks
with the shared variable to avoid duplication and drift.

In `@Configuration/TypoScript/setup.typoscript`:
- Around line 13-20: The three DatabaseQueryProcessor blocks (e.g.,
dataProcessing.10 using TYPO3\CMS\Frontend\DataProcessing\DatabaseQueryProcessor
with table, where, markers.uid.field and orderBy = sorting) duplicate the same
where/marker/sorting settings; extract those shared settings into a single
reusable TypoScript object (e.g., a base processor object) and then include or
reference that base from each dataProcessing entry so each block only overrides
the unique pieces like table and as, reducing drift and keeping
where/markers/orderBy consistent across dataProcessing.10, dataProcessing.26 and
dataProcessing.44.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 35f38f84-6395-493c-bab2-5ab1079ac7cc

📥 Commits

Reviewing files that changed from the base of the PR and between c64fbf8 and 277dc50.

⛔ Files ignored due to path filters (9)
  • Documentation/Images/content-element-wizard.jpg is excluded by !**/*.jpg
  • Resources/Public/Icons/Extension.svg is excluded by !**/*.svg
  • Resources/Public/Icons/content-styleguide-colors.svg is excluded by !**/*.svg
  • Resources/Public/Icons/content-styleguide-fonts.svg is excluded by !**/*.svg
  • Resources/Public/Icons/content-styleguide-headline.svg is excluded by !**/*.svg
  • Resources/Public/Icons/content-styleguide-icons.svg is excluded by !**/*.svg
  • Resources/Public/Icons/content-styleguide-images.svg is excluded by !**/*.svg
  • Resources/Public/Icons/page-styleguide-hidden.svg is excluded by !**/*.svg
  • Resources/Public/Icons/page-styleguide.svg is excluded by !**/*.svg
📒 Files selected for processing (46)
  • .ddev/.setup/scripts/utils.sh
  • .ddev/commands/web/.install-11
  • .ddev/commands/web/.install-12
  • .ddev/commands/web/.install-13
  • .ddev/commands/web/.install-14
  • .ddev/commands/web/11
  • .ddev/config.typo3-setup.yaml
  • .ddev/docker-compose.typo3-setup.yaml
  • .github/workflows/release.yml
  • Classes/Preview/StyleguidePreviewRenderer.php
  • Classes/ViewHelpers/ContrastColorViewHelper.php
  • Classes/ViewHelpers/FilesViewHelper.php
  • Classes/ViewHelpers/Format/FilenameViewHelper.php
  • Classes/ViewHelpers/Render/TemplateViewHelper.php
  • Classes/ViewHelpers/Uri/ResourcePathViewHelper.php
  • Configuration/Icons.php
  • Configuration/TCA/Overrides/tt_content.php
  • Configuration/TCA/tx_metypo3styleguide_color.php
  • Configuration/TCA/tx_metypo3styleguide_font.php
  • Configuration/TCA/tx_metypo3styleguide_image.php
  • Configuration/TsConfig/ContentElements/Colors.tsconfig
  • Configuration/TsConfig/ContentElements/Fonts.tsconfig
  • Configuration/TsConfig/ContentElements/Icons.tsconfig
  • Configuration/TsConfig/ContentElements/Images.tsconfig
  • Configuration/TsConfig/ContentElements/TechnicalHeadline.tsconfig
  • Configuration/TsConfig/TCEFORM.tsconfig
  • Configuration/TypoScript/constants.typoscript
  • Configuration/TypoScript/setup.typoscript
  • Documentation/ContentElement.md
  • Documentation/Patterns.md
  • Documentation/StaticTemplates.md
  • Documentation/ViewHelpers/Classes/TemplateViewHelper.md
  • README.md
  • Resources/Private/Language/de.locallang.xlf
  • Resources/Private/Language/locallang.xlf
  • Resources/Private/Templates/Colors.html
  • Resources/Private/Templates/Fonts.html
  • Resources/Private/Templates/Icons.html
  • Resources/Private/Templates/Images.html
  • Resources/Private/Templates/Patterns/Colors.html
  • Resources/Private/Templates/Patterns/Templates.html
  • Tests/Acceptance/Fixtures/data.xml
  • composer-unused.php
  • composer.json
  • ext_emconf.php
  • ext_tables.sql
💤 Files with no reviewable changes (9)
  • Resources/Private/Templates/Patterns/Colors.html
  • Classes/ViewHelpers/Render/TemplateViewHelper.php
  • Configuration/TypoScript/constants.typoscript
  • Documentation/Patterns.md
  • Documentation/StaticTemplates.md
  • .ddev/commands/web/11
  • Resources/Private/Templates/Patterns/Templates.html
  • Documentation/ViewHelpers/Classes/TemplateViewHelper.md
  • .ddev/commands/web/.install-11

Comment on lines +46 to +52
'type' => 'input',
'size' => 50,
'max' => 255,
'eval' => 'trim',
'required' => true,
'placeholder' => 'EXT:sitepackage/Resources/Public/Images/logo.svg',
],
Copy link

@coderabbitai coderabbitai bot Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

TYPO3 v14 TCA input validation EXT resource paths

💡 Result:

In TYPO3 v14 you should store and validate “system resource identifiers” (not physical paths like typo3conf/ext/...). TYPO3’s System Resource API supports these formats: EXT:ext_key/... (still supported), the recommended PKG:vendor/package:..., FAL:<storageUid>:/..., app resources via PKG:typo3/app:..., and URLs like https://... / URI:/.... Legacy identifiers such as typo3conf/ext/my_ext/... are no longer handled consistently and should be migrated to EXT:/PKG:. [1]

For a TCA type => 'input' field, TYPO3’s built-in validation is limited to the usual eval keywords plus custom eval classes:

  • Use eval => 'is_in' together with the is_in property to whitelist allowed characters (e.g. letters, digits, :, /, ., _, -). [2]
  • For real validation of “allowed resource syntaxes” (EXT/PKG/FAL/URI), implement a custom TCA eval (client + server): TCA eval supports registering Vendor\Extension\... evaluators, executed when saving; is_in is also passed to the evaluator. [2]

If you want to allow app resources, note TYPO3 restricts which app/public folders are allowed by default, and additional allowed folders are configured via $GLOBALS['TYPO3_CONF_VARS']['FE']['addAllowedPaths']. [1]

Sources
[1] TYPO3 v14 Core Changelog – System resource API (Feature #107537) https://docs.typo3.org/c/typo3/cms-core/main/en-us/Changelog/14.0/Feature-107537-SystemResourceAPIForSystemFileAccessAndPublicUriGeneration.html
[2] TYPO3 TCA Reference (main/v14) – type='input', eval, is_in, custom eval classes https://docs.typo3.org/m/typo3/reference-tca/main/en-us/ColumnsConfig/Type/Input/Index.html


🌐 Web query:

TYPO3 TCA validators for file paths and resources

💡 Result:

In TYPO3 TCA there isn’t a single “file path validator” for arbitrary string paths; validation depends on how you model the value (FAL file reference vs. link browser value vs. plain input). These are the usual options:

1) Prefer FAL references: type => 'file' (recommended)

Use the dedicated TCA file field type and restrict by extension via allowed (and optionally disallowed). allowed supports reserved sets like common-image-types, common-text-types, common-media-types, plus explicit extensions. Array notation works reliably in TYPO3 v12.4.1+/v13.1+. [1]

'my_asset' => [
  'config' => [
    'type' => 'file',
    'allowed' => 'common-image-types,svg,pdf',
    // 'disallowed' => 'php,phar', // if needed
    'maxitems' => 1,
  ],
],

This stores a resource reference (sys_file_reference), not a brittle filesystem path. [1]

2) If it’s a link field (page/url/file/folder): type => 'input', renderType => 'inputLink'

Use the link browser control and restrict selectable files by extension using fieldControl.linkPopup.options.allowedExtensions. [2]

'my_link' => [
  'config' => [
    'type' => 'input',
    'renderType' => 'inputLink',
    'fieldControl' => [
      'linkPopup' => [
        'options' => [
          'allowedExtensions' => 'pdf,docx',
          // optionally hide link modes:
          // 'blindLinkOptions' => 'folder',
        ],
      ],
    ],
  ],
],

Note: this validates the link browser selection options; the stored value is still a link string, not a FAL reference. [2]

3) If you list files from a folder in a select box: fileFolderConfig.allowedExtensions

For type => 'select' with fileFolderConfig, TYPO3 can filter the list of selectable files by extension using fileFolderConfig.allowedExtensions. [3]

4) Plain string “path” field: use eval (or a custom eval)

config.eval for type='input' / type='text' supports many built-in evaluations, and you can register custom form eval classes via $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tce']['formevals'] and then reference the class name in eval. [4]
So if you truly must store a path-like string, implement a custom eval that enforces “relative only”, “no ..”, allowed prefixes, etc. (TYPO3 core doesn’t ship a dedicated “filesystem path” eval keyword). [4]

Related (often confused with TCA): Extbase upload validators

If you’re handling uploads via Extbase, TYPO3 mentions default validators like FileNameValidator and FileExtensionMimeTypeConsistencyValidator, but that’s Extbase runtime validation, not TCA field validation. [5]

Sources:
[1] TCA type file / allowed (TYPO3 TCA Reference) (docs.typo3.org)
[2] inputLink / linkPopup.allowedExtensions (TYPO3 TCA Reference) (docs.typo3.org)
[3] fileFolderConfig.allowedExtensions (TYPO3 TCA Reference) (docs.typo3.org)
[4] eval and custom form eval registration (TYPO3 TCA Reference) (docs.typo3.org)
[5] Extbase file upload validators + note about TCA type=file allowed extensions (TYPO3 Core API docs) (docs.typo3.org)

Citations:


🌐 Web query:

TYPO3 v14 TCA input field eval validators options

💡 Result:

In TYPO3 v14, a TCA field with config['type'] = 'input' supports these eval keywords (comma-separated, executed in the given order) [1]:

  • alpha (a–z, A–Z only)
  • alphanum (letters + digits)
  • alphanum_x (letters + digits + _ + -)
  • domainname (valid domain, converts to punycode when needed)
  • is_in (filters to the characters given in config['is_in'])
  • lower / upper
  • md5
  • nospace
  • num (digits only)
  • trim
  • unique (unique within table, server-side)
  • uniqueInPid (unique within current page/PID, server-side)
  • year (still available, but changed/legacy; TYPO3 recommends using type='number' or type='datetime' instead) [1]

Notable v14 change: “required” is not an eval rule here; use config['required'] => true [2].

Example

'config' => [
  'type' => 'input',
  'required' => true,
  'eval' => 'trim,nospace,lower,uniqueInPid',
],

You can also add custom eval rules by registering a PHP class in $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tce']['formevals'] and referencing the class name in eval [1].

Sources: [1] [2]


🏁 Script executed:

fd tx_metypo3styleguide_image.php --type f

Repository: move-elevator/typo3-styleguide

Length of output: 122


🏁 Script executed:

cat Configuration/TCA/tx_metypo3styleguide_image.php

Repository: move-elevator/typo3-styleguide

Length of output: 2122


🏁 Script executed:

rg "tx_metypo3styleguide_image" --type php -A 5 -B 5 | head -100

Repository: move-elevator/typo3-styleguide

Length of output: 6173


🏁 Script executed:

rg "path" Configuration/TCA/tx_metypo3styleguide_image.php -A 2 -B 2

Repository: move-elevator/typo3-styleguide

Length of output: 626


🏁 Script executed:

fd -e html -e fluid -e php -type f | xargs rg "tx_metypo3styleguide_image|path" | head -50

Repository: move-elevator/typo3-styleguide

Length of output: 8508


🏁 Script executed:

cat Resources/Private/Templates/Images.html

Repository: move-elevator/typo3-styleguide

Length of output: 1342


🏁 Script executed:

rg "resourcePath|uri" Classes/ -A 3 -B 3 | head -100

Repository: move-elevator/typo3-styleguide

Length of output: 56


🏁 Script executed:

find Classes -name "*resourcePath*" -o -name "*uri*" | head -20

Repository: move-elevator/typo3-styleguide

Length of output: 56


🏁 Script executed:

rg "uri\.resourcePath|resourcePath" Classes/ -B 5 -A 10

Repository: move-elevator/typo3-styleguide

Length of output: 56


🏁 Script executed:

fd -name "*.php" Classes/ViewHelpers | xargs ls -la

Repository: move-elevator/typo3-styleguide

Length of output: 2220


🏁 Script executed:

find Classes/ViewHelpers -name "*.php"

Repository: move-elevator/typo3-styleguide

Length of output: 308


🏁 Script executed:

cat Classes/ViewHelpers/Uri/ResourcePathViewHelper.php

Repository: move-elevator/typo3-styleguide

Length of output: 1084


Add validation for path field to enforce valid resource syntax.

The field currently accepts arbitrary strings but rendering passes them directly to GeneralUtility::getFileAbsFileName() without validation. Enforce EXT: path format using TCA eval with a character whitelist (is_in) or a custom eval class to prevent malformed paths from being persisted and failing at runtime in the view helper.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Configuration/TCA/tx_metypo3styleguide_image.php` around lines 46 - 52, The
TCA for the 'path' field currently only trims input and allows arbitrary
strings; update the field definition in tx_metypo3styleguide_image.php (the
array entry for 'path') to validate EXT: resource syntax by adding a stricter
eval: either include a whitelist check (e.g. use an is_in-style eval if you
register allowed prefixes) or, preferably, register and use a custom eval class
(e.g. Vendor\YourExt\Evaluation\ExtPathEval) and add its token to the eval
string (for example 'eval' => 'trim,required,tx_yourext_extpatheval') so only
valid "EXT:extensionKey/..." paths are persisted and passed to
GeneralUtility::getFileAbsFileName().

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@coderabbitai The path field stores EXT: resource identifiers resolved at render time via GeneralUtility::getFileAbsFileName(). TYPO3 TCA has no built-in eval for resource path validation, and adding custom validation for a styleguide tool would be over-engineering. Invalid paths simply won't render.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@konradmichalik, that's a fair point — for a developer-facing styleguide tool, silent graceful failure via GeneralUtility::getFileAbsFileName() returning an empty/null path on invalid input is a perfectly acceptable outcome. Acknowledged, I'll drop this suggestion.


✏️ Learnings added
Learnt from: konradmichalik
Repo: move-elevator/typo3-styleguide PR: 31
File: Configuration/TCA/tx_metypo3styleguide_image.php:46-52
Timestamp: 2026-03-05T12:59:37.991Z
Learning: In the `move-elevator/typo3-styleguide` TYPO3 extension, the `path` field in `Configuration/TCA/tx_metypo3styleguide_image.php` (and similar image/icon path fields) intentionally stores raw `EXT:` resource identifiers without custom TCA eval validation. Invalid paths fail gracefully at render time via `GeneralUtility::getFileAbsFileName()`. Adding a custom eval class for path validation is considered over-engineering for this developer-facing styleguide tool.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment on lines +15 to +17
<f:variable name="contrastColor">
<sg:contrastColor color="{color.data.color}"/>
</f:variable>
Copy link

@coderabbitai coderabbitai bot Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Invalid color input can hard-fail rendering.

Line 16 invokes sg:contrastColor on editor-provided data; malformed HEX values trigger an exception in Classes/ViewHelpers/ContrastColorViewHelper.php, which can break frontend rendering for this CE. Please add validation/fail-soft handling for invalid color values.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Resources/Private/Templates/Colors.html` around lines 15 - 17, The template
currently calls sg:contrastColor with unvalidated editor input (variable
contrastColor using {color.data.color}), which can throw in
Classes/ViewHelpers/ContrastColorViewHelper.php for malformed HEX values; update
the Colors.html template to validate or sanitize {color.data.color} before
calling sg:contrastColor and wrap the helper invocation in a fail-soft fallback:
check the string format (e.g., regex for `#RRGGBB` or `#RGB`) and only call
sg:contrastColor when valid, otherwise set contrastColor to a safe default
(e.g., "#000000" or computed default) so rendering never throws; ensure the
change references the contrastColor variable and uses the same sg:contrastColor
helper name.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@coderabbitai The template already guards with <f:if condition="{color.data.color}">, and the TCA field is required with max => 7. An editor would need to intentionally enter malformed data. For a developer-facing styleguide tool, failing loudly on invalid hex codes is the correct behavior — it surfaces misconfiguration immediately rather than silently degrading.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rate Limit Exceeded

@konradmichalik have exceeded the limit for the number of chat messages per hour. Please wait 1 minutes and 57 seconds before sending another message.

<div class="typo3-styleguide__image typo3-styleguide__icon">
<div class="typo3-styleguide__image-box">
<img src="{f:uri.resource(path: '{path}{file}')}" alt="{file}"/>
<img src="{sg:uri.resourcePath(path: '{data.tx_metypo3styleguide_icons_path}{file}')}" alt="{file}"/>
Copy link

@coderabbitai coderabbitai bot Mar 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Normalize the icon base path before concatenation.

On Line 17, the template assumes data.tx_metypo3styleguide_icons_path always ends with /. If an editor saves EXT:.../Icons (no trailing slash), generated URLs break for all icons.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Resources/Private/Templates/Icons.html` at line 17, Normalize
data.tx_metypo3styleguide_icons_path before concatenation so it always has
exactly one trailing slash; update the template that builds the src (the <img>
using {sg:uri.resourcePath(path:
'{data.tx_metypo3styleguide_icons_path}{file}')} ) to trim any trailing slash
from data.tx_metypo3styleguide_icons_path and then append a single "/" before
{file} (or use a ViewHelper that joins path segments), ensuring the resourcePath
always receives a valid path with one separator between base path and file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@coderabbitai Good catch. Added an explicit / separator between the base path and filename in the template to handle paths without trailing slash.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rate Limit Exceeded

@konradmichalik have exceeded the limit for the number of chat messages per hour. Please wait 1 minutes and 56 seconds before sending another message.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
README.md (1)

8-8: ⚠️ Potential issue | 🟡 Minor

Fix the empty badge link target.

Line [8] still uses an empty destination, so the badge is non-functional in rendered Markdown.

Suggested fix
-[![Supported TYPO3 versions](https://badgen.net/badge/TYPO3/12%20&%2013%20&%2014/orange)]()
+[![Supported TYPO3 versions](https://badgen.net/badge/TYPO3/12%20&%2013%20&%2014/orange)](https://extensions.typo3.org/extension/typo3_styleguide)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.md` at line 8, The badge link in README.md uses an empty destination
('[![Supported TYPO3 versions](...)]()'), making it non-functional; update that
Markdown link by replacing the empty link target with the correct URL (e.g., the
project’s TYPO3 support page, release notes, or https://typo3.org) so the badge
points to the intended documentation or homepage and remains clickable; edit the
exact line containing the '[![Supported TYPO3
versions](https://badgen.net/badge/TYPO3/12%20&%2013%20&%2014/orange)]()' markup
to include the chosen URL.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@README.md`:
- Line 8: The badge link in README.md uses an empty destination ('[![Supported
TYPO3 versions](...)]()'), making it non-functional; update that Markdown link
by replacing the empty link target with the correct URL (e.g., the project’s
TYPO3 support page, release notes, or https://typo3.org) so the badge points to
the intended documentation or homepage and remains clickable; edit the exact
line containing the '[![Supported TYPO3
versions](https://badgen.net/badge/TYPO3/12%20&%2013%20&%2014/orange)]()' markup
to include the chosen URL.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4d28ffef-60a3-4bcd-9ac5-fe817c9b5494

📥 Commits

Reviewing files that changed from the base of the PR and between 277dc50 and 1ca0ab5.

⛔ Files ignored due to path filters (1)
  • composer.lock is excluded by !**/*.lock
📒 Files selected for processing (1)
  • README.md

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
Classes/Preview/StyleguidePreviewRenderer.php (1)

99-107: Consider CSS property validation for defense-in-depth.

The font and weight values are inserted into inline CSS. While htmlspecialchars prevents attribute breakout, it doesn't prevent CSS property injection (e.g., Arial; background:url(//x)). Since this is backend-only for trusted editors, the risk is minimal, but a simple regex check could add a safety layer.

🔧 Optional: Add basic validation
+    /**
+     * Basic CSS value sanitizer - removes characters that could inject additional properties.
+     */
+    private function sanitizeCssValue(string $value): string
+    {
+        return preg_replace('/[;\{\}:]/', '', $value) ?? '';
+    }
+
     private function renderFontsPreview(array $row): string
     {
         // ...
-            $font = htmlspecialchars((string) $child['font'], ENT_QUOTES);
-            $weight = htmlspecialchars((string) $child['font_weight'], ENT_QUOTES);
+            $font = htmlspecialchars($this->sanitizeCssValue((string) $child['font']), ENT_QUOTES);
+            $weight = htmlspecialchars($this->sanitizeCssValue((string) $child['font_weight']), ENT_QUOTES);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Classes/Preview/StyleguidePreviewRenderer.php` around lines 99 - 107, The
inline CSS is built from unvalidated $font and $weight in
StyleguidePreviewRenderer (variables $font, $weight, $label, $displayName,
$style), which allows CSS injection; validate and sanitize before concatenation
by applying a whitelist check: allow $font to match a safe regex (e.g. letters,
digits, spaces, commas, hyphens, quotes and parentheses) or else replace with a
safe default like "sans-serif", and allow $weight only if it matches numeric
weights (100-900) or keywords (normal,bold,bolder,lighter) otherwise set to
"normal"; after validation still pass through htmlspecialchars and then build
$style from the validated values so only safe values are inserted into the
inline CSS.
.ddev/.setup/scripts/utils.sh (1)

373-385: Extract shared post-setup mutations to avoid drift.

post_setup_11 and post_setup_12 duplicate the same sed adjustments. Please move shared mutations into one helper with a version-specific config path argument.

♻️ Minimal refactor sketch
+function apply_common_site_adjustments() {
+  local deprecations_file=$1
+  sed -i "/'deprecations'/,/^[[:space:]]*'disabled' => true,/s/'disabled' => true,/'disabled' => false,/" "$deprecations_file"
+  sed -i -e "s/base: ht\//base: \//g" "/var/www/html/.Build/$VERSION/config/sites/main/config.yaml"
+  sed -i -e 's/base: \/en\//base: \//g' "/var/www/html/.Build/$VERSION/config/sites/main/config.yaml"
+}
+
 function post_setup_11 {
   $TYPO3_BIN install:setup -n --database-name $DATABASE
   setup_typo3
   $TYPO3_BIN configuration:set 'GFX/processor_path_lzw' '/usr/bin/'
-  sed -i "/'deprecations'/,/^[[:space:]]*'disabled' => true,/s/'disabled' => true,/'disabled' => false,/" /var/www/html/.Build/$VERSION/public/typo3conf/LocalConfiguration.php
-  sed -i -e "s/base: ht\//base: \//g" /var/www/html/.Build/$VERSION/config/sites/main/config.yaml
-  sed -i -e 's/base: \/en\//base: \//g' /var/www/html/.Build/$VERSION/config/sites/main/config.yaml
+  apply_common_site_adjustments "/var/www/html/.Build/$VERSION/public/typo3conf/LocalConfiguration.php"
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.ddev/.setup/scripts/utils.sh around lines 373 - 385, post_setup_11 and
post_setup_12 duplicate the same sed-based mutations; extract those shared sed
calls into a new helper (e.g., apply_post_setup_mutations or post_setup_common)
that accepts the version-specific config path (the LocalConfiguration.php and
config/sites/main/config.yaml paths) and replace the inline sed lines in both
post_setup_11 and post_setup_12 with a call to this helper, passing the
appropriate /var/www/html/.Build/$VERSION/... paths so all shared edits are
centralized.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.ddev/.setup/scripts/utils.sh:
- Around line 163-165: post_setup() currently dispatches to post_setup_11 when
VERSION == "11" via the post_setup_11 symbol, but there is no corresponding
.ddev/commands/web/.install-11 entrypoint; either add that missing .install-11
script in .ddev/commands/web/ implementing whatever post_setup_11 expects, or
remove the VERSION == "11" branch and delete/rename post_setup_11 to avoid
calling a non-existent dispatcher; update any references to post_setup_11 to
match the chosen approach so ddev install 11 no longer fails.

---

Nitpick comments:
In @.ddev/.setup/scripts/utils.sh:
- Around line 373-385: post_setup_11 and post_setup_12 duplicate the same
sed-based mutations; extract those shared sed calls into a new helper (e.g.,
apply_post_setup_mutations or post_setup_common) that accepts the
version-specific config path (the LocalConfiguration.php and
config/sites/main/config.yaml paths) and replace the inline sed lines in both
post_setup_11 and post_setup_12 with a call to this helper, passing the
appropriate /var/www/html/.Build/$VERSION/... paths so all shared edits are
centralized.

In `@Classes/Preview/StyleguidePreviewRenderer.php`:
- Around line 99-107: The inline CSS is built from unvalidated $font and $weight
in StyleguidePreviewRenderer (variables $font, $weight, $label, $displayName,
$style), which allows CSS injection; validate and sanitize before concatenation
by applying a whitelist check: allow $font to match a safe regex (e.g. letters,
digits, spaces, commas, hyphens, quotes and parentheses) or else replace with a
safe default like "sans-serif", and allow $weight only if it matches numeric
weights (100-900) or keywords (normal,bold,bolder,lighter) otherwise set to
"normal"; after validation still pass through htmlspecialchars and then build
$style from the validated values so only safe values are inserted into the
inline CSS.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 42143e17-b1cc-4536-ba14-ea1f9e1697cd

📥 Commits

Reviewing files that changed from the base of the PR and between 1ca0ab5 and a9accac.

⛔ Files ignored due to path filters (1)
  • composer.lock is excluded by !**/*.lock
📒 Files selected for processing (6)
  • .ddev/.setup/scripts/utils.sh
  • .ddev/commands/web/.install-12
  • Classes/Preview/StyleguidePreviewRenderer.php
  • README.md
  • Resources/Private/Templates/Icons.html
  • composer.json
🚧 Files skipped from review as they are similar to previous changes (3)
  • Resources/Private/Templates/Icons.html
  • README.md
  • .ddev/commands/web/.install-12

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
Classes/Preview/StyleguidePreviewRenderer.php (1)

128-143: Filter icon candidates to image file types before counting/rendering.

On Lines 128-143, every regular file is treated as an icon. Non-image files can consume the 20-item cap and produce broken thumbnails.

♻️ Proposed refinement
         foreach ($files as $file) {
             if ('.' === $file || '..' === $file || !is_file($absDir.'/'.$file)) {
                 continue;
             }
+            $extension = strtolower((string) pathinfo($file, PATHINFO_EXTENSION));
+            if (!in_array($extension, ['svg', 'png', 'jpg', 'jpeg', 'gif', 'webp'], true)) {
+                continue;
+            }
 
             ++$count;
             if ($count > 20) {
                 $hasMore = true;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Classes/Preview/StyleguidePreviewRenderer.php` around lines 128 - 143, The
loop treating every regular file as an icon must skip non-image files before
incrementing $count or pushing into $icons; update the foreach that iterates
$files (using $absDir) to test each candidate is an image (e.g. check extension
against a whitelist like jpg/jpeg/png/gif/svg or use getimagesize/finfo to
confirm image MIME) and only then ++$count and append the array with 'name' and
'webPath' (keep using PathUtility::getAbsoluteWebPath($absDir.'/'.$file));
ensure the 20-item cap ($count > 20) applies only to validated image entries so
non-image files do not consume the limit.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Classes/Preview/StyleguidePreviewRenderer.php`:
- Around line 161-168: The webPath generation should only use
PathUtility::getAbsoluteWebPath when the resolved absolute path actually points
to a file; update the block that builds the $images[] entry (using $extPath and
$absPath) to guard creation of 'webPath' with is_file($absPath) instead of just
checking that $absPath is non-empty, so missing files will yield an empty
webPath and the template can fall back to {image.path}.

---

Nitpick comments:
In `@Classes/Preview/StyleguidePreviewRenderer.php`:
- Around line 128-143: The loop treating every regular file as an icon must skip
non-image files before incrementing $count or pushing into $icons; update the
foreach that iterates $files (using $absDir) to test each candidate is an image
(e.g. check extension against a whitelist like jpg/jpeg/png/gif/svg or use
getimagesize/finfo to confirm image MIME) and only then ++$count and append the
array with 'name' and 'webPath' (keep using
PathUtility::getAbsoluteWebPath($absDir.'/'.$file)); ensure the 20-item cap
($count > 20) applies only to validated image entries so non-image files do not
consume the limit.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f36d3afe-f221-49cd-bc25-c8bcaa12ab21

📥 Commits

Reviewing files that changed from the base of the PR and between 9c625ed and 1e6e8b0.

📒 Files selected for processing (8)
  • Classes/Preview/StyleguidePreviewRenderer.php
  • Configuration/TsConfig/TCEFORM.tsconfig
  • Resources/Private/Templates/Preview/Colors.html
  • Resources/Private/Templates/Preview/Fonts.html
  • Resources/Private/Templates/Preview/Icons.html
  • Resources/Private/Templates/Preview/Images.html
  • composer-unused.php
  • composer.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • composer.json

@konradmichalik konradmichalik merged commit f6325fd into main Mar 5, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TYPO3 14 compatibility

1 participant