From e091b3e750ed40bb3049b81d1c255d1b32613b49 Mon Sep 17 00:00:00 2001 From: David Steeb Date: Fri, 22 May 2026 16:58:55 +0000 Subject: [PATCH 1/5] Modernize for TYPO3 v13/v14 Drop support for TYPO3 10/11/12 and PHP < 8.2. * Remove dead code: ext_tables.sql (TCA auto-schema), ext_localconf.php (legacy pre-v12 hook + duplicate icon registration), Classes/Hooks/ (PageLayoutViewDrawItemHookInterface removed in v12), Classes/Listener/ PageContentPreviewRendering (now handled in ContentPreviewRenderer). * Ship configuration as a Site Set under Configuration/Sets/codeblock/ instead of relying on integrators to @import TypoScript and PageTSconfig. * Use constructor injection for Highlighter in HighlightProcessor and CodeLanguages; mark both as public services so TYPO3 can resolve them. * Fix the auto-detect default for code_language (?? true was wrong). * Truncate the page-module preview with mb_strimwidth. * Refresh GitHub Actions workflow to ubuntu-latest, checkout@v4, PHP 8.2, GITHUB_OUTPUT syntax. * Rewrite README, add CHANGELOG documenting 3.0.0 breaking changes. --- .github/workflows/publish.yml | 31 ++-- CHANGELOG.md | 48 +++++ .../Preview/ContentPreviewRenderer.php | 22 ++- Classes/DataProcessing/HighlightProcessor.php | 63 +++---- Classes/DataProvider/CodeLanguages.php | 38 ++-- Classes/Hooks/CodeblockPreviewRenderer.php | 50 ----- .../Listener/PageContentPreviewRendering.php | 31 ---- Configuration/Services.yaml | 9 +- Configuration/Sets/codeblock/config.yaml | 4 + .../codeblock/page.tsconfig} | 6 +- .../codeblock}/setup.typoscript | 1 - Configuration/TCA/Overrides/tt_content.php | 123 ++++++------- README.md | 172 +++++++----------- composer.json | 24 +-- ext_emconf.php | 5 +- ext_localconf.php | 19 -- ext_tables.sql | 3 - 17 files changed, 282 insertions(+), 367 deletions(-) create mode 100644 CHANGELOG.md delete mode 100644 Classes/Hooks/CodeblockPreviewRenderer.php delete mode 100644 Classes/Listener/PageContentPreviewRendering.php create mode 100644 Configuration/Sets/codeblock/config.yaml rename Configuration/{PageTs/PageTs.tsconfig => Sets/codeblock/page.tsconfig} (82%) rename Configuration/{TypoScript => Sets/codeblock}/setup.typoscript (98%) delete mode 100644 ext_localconf.php delete mode 100644 ext_tables.sql diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 32af89f..cc3ddce 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -2,47 +2,54 @@ name: publish on: push: - tag: + tags: + - '*' jobs: publish: name: Publish new version to TER if: startsWith(github.ref, 'refs/tags/') - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest env: TYPO3_EXTENSION_KEY: ${{ secrets.TYPO3_EXTENSION_KEY }} TYPO3_API_TOKEN: ${{ secrets.TYPO3_API_TOKEN }} steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Check tag run: | - if ! [[ ${{ github.ref }} =~ ^refs/tags/[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$ ]]; then + if ! [[ ${{ github.ref }} =~ ^refs/tags/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then exit 1 fi + - name: Get version id: get-version - run: echo ::set-output name=version::${GITHUB_REF/refs\/tags\//} + run: echo "version=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT" - name: Get comment id: get-comment run: | - readonly local comment=$(git tag -n10 -l ${{ steps.get-version.outputs.version }} | sed "s/^[0-9.]*[ ]*//g") + comment=$(git tag -n10 -l "${{ steps.get-version.outputs.version }}" | sed "s/^[0-9.]*[ ]*//g") if [[ -z "${comment// }" ]]; then - echo ::set-output name=comment::Released version ${{ steps.get-version.outputs.version }} of ${{ env.TYPO3_EXTENSION_KEY }} - else - echo ::set-output name=comment::$comment + comment="Released version ${{ steps.get-version.outputs.version }} of ${{ env.TYPO3_EXTENSION_KEY }}" fi + { + echo "comment<> "$GITHUB_OUTPUT" + - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: 7.4 + php-version: '8.2' extensions: intl, mbstring, json, zip, curl + tools: composer:v2 - name: Install tailor - run: composer global require typo3/tailor --prefer-dist --no-progress --no-suggest + run: composer global require typo3/tailor --prefer-dist --no-progress - name: Publish to TER - run: php ~/.composer/vendor/bin/tailor ter:publish --comment "${{ steps.get-comment.outputs.comment }}" ${{ steps.get-version.outputs.version }} \ No newline at end of file + run: php ~/.composer/vendor/bin/tailor ter:publish --comment "${{ steps.get-comment.outputs.comment }}" "${{ steps.get-version.outputs.version }}" diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..4d041a2 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,48 @@ +# Changelog + +## 3.0.0 — Cleanup release for TYPO3 v13 and v14 + +### Breaking + +- **Drops support for TYPO3 10, 11, and 12.** Minimum is now TYPO3 13.4. +- **Requires PHP 8.2 or newer.** +- **Delivery is now a Site Set.** Assign `b13/codeblock` to your site + (Sites module → tab *Sets for this Site*) instead of `@import`-ing + `EXT:codeblock/Configuration/TypoScript/setup.typoscript` and + `EXT:codeblock/Configuration/PageTs/PageTs.tsconfig`. The legacy file + paths no longer exist. + +### Removed + +- `ext_tables.sql` — the `code_language` column is now auto-created from + the TCA definition (TYPO3 v13+ feature). +- `ext_localconf.php` — the pre-v12 `tt_content_drawItem` hook and the + manual `IconRegistry` registration are gone. The icon is registered via + `Configuration/Icons.php`. +- `Classes/Hooks/CodeblockPreviewRenderer.php` — relied on the + `PageLayoutViewDrawItemHookInterface`, which was removed in TYPO3 v12. +- `Classes/Listener/PageContentPreviewRendering.php` — preview truncation + now happens inside `ContentPreviewRenderer::renderPageModulePreviewContent()`, + so the separate event listener is no longer needed. +- `Configuration/PageTs/PageTs.tsconfig` and + `Configuration/TypoScript/setup.typoscript` — moved into the Site Set. + +### Changed + +- `HighlightProcessor` and `CodeLanguages` now get their `Highlighter` + instance via constructor injection (autowired through `Services.yaml`). +- `HighlightProcessor` fixes the auto-detect default for the + `code_language` field. The previous `?? true` default unintentionally + skipped auto-detection when the field was unset. +- `ContentPreviewRenderer` truncates the bodytext for the page-module + preview with `mb_strimwidth()` instead of the deprecated + `GeneralUtility::fixed_lgd_cs()`. +- TCA `CType` registration now includes a `description` and `group`, so + the New Content Element Wizard picks it up correctly under the + *default* group on TYPO3 13 and 14. +- GitHub Actions workflow refreshed: `ubuntu-latest`, `actions/checkout@v4`, + PHP 8.2, `$GITHUB_OUTPUT` syntax. + +## 2.1.0 and earlier + +See git history. diff --git a/Classes/Backend/Preview/ContentPreviewRenderer.php b/Classes/Backend/Preview/ContentPreviewRenderer.php index 5b0c65c..68d341e 100644 --- a/Classes/Backend/Preview/ContentPreviewRenderer.php +++ b/Classes/Backend/Preview/ContentPreviewRenderer.php @@ -12,20 +12,34 @@ * of the License, or any later version. */ +use TYPO3\CMS\Backend\Preview\StandardContentPreviewRenderer; use TYPO3\CMS\Backend\View\BackendLayout\Grid\GridColumnItem; use TYPO3\CMS\Core\Domain\RecordInterface; -class ContentPreviewRenderer extends \TYPO3\CMS\Backend\Preview\StandardContentPreviewRenderer +/** + * Renders a preview of the "codeblock" content element for the page module. + * + * Shows a truncated version of the entered code (rather than the default + * "no preview available" placeholder) so editors can recognize the element + * at a glance. + */ +final class ContentPreviewRenderer extends StandardContentPreviewRenderer { + private const PREVIEW_MAX_LENGTH = 1000; + public function renderPageModulePreviewContent(GridColumnItem $item): string { $record = $item->getRecord(); $bodytext = $record instanceof RecordInterface ? (string)($record->get('bodytext') ?? '') : (string)($record['bodytext'] ?? ''); - if (trim($bodytext) !== '') { - return $this->linkEditContent(nl2br(htmlentities($bodytext)), $record) . '
'; + + if (trim($bodytext) === '') { + return parent::renderPageModulePreviewContent($item); } - return parent::renderPageModulePreviewContent($item); + + $bodytext = mb_strimwidth($bodytext, 0, self::PREVIEW_MAX_LENGTH, '…'); + + return $this->linkEditContent(nl2br(htmlentities($bodytext)), $record) . '
'; } } diff --git a/Classes/DataProcessing/HighlightProcessor.php b/Classes/DataProcessing/HighlightProcessor.php index b88292e..db28901 100644 --- a/Classes/DataProcessing/HighlightProcessor.php +++ b/Classes/DataProcessing/HighlightProcessor.php @@ -13,51 +13,46 @@ */ use Highlight\Highlighter; -use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Frontend\ContentObject\ContentDataProcessor; use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; use TYPO3\CMS\Frontend\ContentObject\DataProcessorInterface; /** - * This processor passes the given text through highlight.php. + * Runs the configured field's value through highlight.php and returns + * the rendered HTML plus the detected language for use in Fluid. */ -class HighlightProcessor implements DataProcessorInterface +final class HighlightProcessor implements DataProcessorInterface { - protected ContentDataProcessor $contentDataProcessor; - - public function __construct(ContentDataProcessor $contentDataProcessor) - { - $this->contentDataProcessor = $contentDataProcessor; + public function __construct( + private readonly Highlighter $highlighter + ) { } - /** - * Fetches records from the database as an array - * - * @param ContentObjectRenderer $cObj The data of the content element or page - * @param array $contentObjectConfiguration The configuration of Content Object - * @param array $processorConfiguration The configuration of this processor - * @param array $processedData Key/value store of processed data (e.g. to be passed to a Fluid View) - * - * @return array the processed data as key/value store - */ - public function process(ContentObjectRenderer $cObj, array $contentObjectConfiguration, array $processorConfiguration, array $processedData) - { - $fieldName = $processorConfiguration['field']; - $targetVariableName = $cObj->stdWrapValue('as', $processorConfiguration, 'bodytext_formatted'); - $highlight = GeneralUtility::makeInstance(Highlighter::class); - - // Let highlight.php decide which code language to use from all registered if "detect automatically" is selected. - if (!($processedData['data']['code_language'] ?? true)) { - $languages = $highlight->listLanguages(); - $highlight->setAutodetectLanguages($languages); - $highlighted = $highlight->highlightAuto($processedData['data'][$fieldName]); + public function process( + ContentObjectRenderer $cObj, + array $contentObjectConfiguration, + array $processorConfiguration, + array $processedData + ): array { + $fieldName = (string)($processorConfiguration['field'] ?? 'bodytext'); + $targetVariableName = (string)$cObj->stdWrapValue('as', $processorConfiguration, 'bodytext_formatted'); + + $source = (string)($processedData['data'][$fieldName] ?? ''); + $language = (string)($processedData['data']['code_language'] ?? ''); + + if ($language === '') { + // Let highlight.php pick the language from all registered ones. + $this->highlighter->setAutodetectLanguages($this->highlighter->listLanguages()); + $highlighted = $this->highlighter->highlightAuto($source); } else { - $highlighted = $highlight->highlight($processedData['data']['code_language'], $processedData['data'][$fieldName]); + $highlighted = $this->highlighter->highlight($language, $source); } - $processedData[$targetVariableName]['code'] = $highlighted->value; - $processedData[$targetVariableName]['language'] = $highlighted->language; - $processedData[$targetVariableName]['lines'] = preg_split('/\r\n|\r|\n/', $highlighted->value); + $processedData[$targetVariableName] = [ + 'code' => $highlighted->value, + 'language' => $highlighted->language, + 'lines' => preg_split('/\r\n|\r|\n/', $highlighted->value) ?: [], + ]; + return $processedData; } } diff --git a/Classes/DataProvider/CodeLanguages.php b/Classes/DataProvider/CodeLanguages.php index 715d84a..15b2c99 100644 --- a/Classes/DataProvider/CodeLanguages.php +++ b/Classes/DataProvider/CodeLanguages.php @@ -13,31 +13,33 @@ */ use Highlight\Highlighter; -use TYPO3\CMS\Core\Utility\GeneralUtility; /** - * Class which provides a function to get all languages known by highlight.php. + * Populates the "code_language" select field with all languages + * known to highlight.php plus an "auto detect" entry. */ -class CodeLanguages +final class CodeLanguages { + public function __construct( + private readonly Highlighter $highlighter + ) { + } + /** - * @param array $config - * @return array + * itemsProcFunc callback for the "code_language" TCA select field. */ - public function getAll($config): array + public function getAll(array &$config): void { - // Get all languages from highlight.php. - $highlight = GeneralUtility::makeInstance(Highlighter::class); - $languages = $highlight->listLanguages(); - - // Add default to items as the highlight processor can handle the automatic detection of the language. - $config['items'][] = ['LLL:EXT:codeblock/Resources/Private/Language/locallang_db.xlf:tt_content.code_language.detect_automatically', '']; - - // Add all languages to dropdown. - foreach ($languages as $language) { - $config['items'][] = [$language, $language]; + $config['items'][] = [ + 'label' => 'LLL:EXT:codeblock/Resources/Private/Language/locallang_db.xlf:tt_content.code_language.detect_automatically', + 'value' => '', + ]; + + foreach ($this->highlighter->listLanguages() as $language) { + $config['items'][] = [ + 'label' => $language, + 'value' => $language, + ]; } - - return $config; } } diff --git a/Classes/Hooks/CodeblockPreviewRenderer.php b/Classes/Hooks/CodeblockPreviewRenderer.php deleted file mode 100644 index 2bf3378..0000000 --- a/Classes/Hooks/CodeblockPreviewRenderer.php +++ /dev/null @@ -1,50 +0,0 @@ -isFeatureEnabled('fluidBasedPageModule') === false) { - $itemContent .= $parentObject->linkEditContent(nl2br(htmlentities($bodytext)), $row) . '
'; - $drawItem = false; - } - } - } - } -} diff --git a/Classes/Listener/PageContentPreviewRendering.php b/Classes/Listener/PageContentPreviewRendering.php deleted file mode 100644 index 98b7486..0000000 --- a/Classes/Listener/PageContentPreviewRendering.php +++ /dev/null @@ -1,31 +0,0 @@ -getRecord(); - if ((new Typo3Version())->getMajorVersion() < 14) { - if (($record['CType'] ?? '') === 'codeblock' && trim($record['bodytext'] ?? '') !== '') { - $record['bodytext'] = GeneralUtility::fixed_lgd_cs($record['bodytext'], 1000); - $event->setRecord($record); - } - } - } -} diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml index 2d566c5..e247880 100644 --- a/Configuration/Services.yaml +++ b/Configuration/Services.yaml @@ -7,9 +7,10 @@ services: B13\Codeblock\: resource: '../Classes/*' + # DataProcessors are resolved via TypoScript and instantiated by core, so they need public visibility. B13\Codeblock\DataProcessing\HighlightProcessor: public: true - B13\Codeblock\Listener\PageContentPreviewRendering: - tags: - - name: event.listener - identifier: 'b13/codeblock/page-content-preview-rendering' + + # itemsProcFunc callbacks are resolved by class name and need public visibility, too. + B13\Codeblock\DataProvider\CodeLanguages: + public: true diff --git a/Configuration/Sets/codeblock/config.yaml b/Configuration/Sets/codeblock/config.yaml new file mode 100644 index 0000000..5aa9c4d --- /dev/null +++ b/Configuration/Sets/codeblock/config.yaml @@ -0,0 +1,4 @@ +name: b13/codeblock +label: 'b13: Code Block content element' +dependencies: + - typo3/fluid-styled-content diff --git a/Configuration/PageTs/PageTs.tsconfig b/Configuration/Sets/codeblock/page.tsconfig similarity index 82% rename from Configuration/PageTs/PageTs.tsconfig rename to Configuration/Sets/codeblock/page.tsconfig index 976e10e..891ef73 100644 --- a/Configuration/PageTs/PageTs.tsconfig +++ b/Configuration/Sets/codeblock/page.tsconfig @@ -1,11 +1,11 @@ mod.wizards.newContentElement { wizardItems { - common.elements.codeblock { + default.elements.codeblock { + iconIdentifier = content-codeblock title = LLL:EXT:codeblock/Resources/Private/Language/locallang_db.xlf:tt_content.CType description = LLL:EXT:codeblock/Resources/Private/Language/locallang_db.xlf:tt_content.wizard.description tt_content_defValues.CType = codeblock - iconIdentifier = content-codeblock } - common.show := addToList(codeblock) + default.show := addToList(codeblock) } } diff --git a/Configuration/TypoScript/setup.typoscript b/Configuration/Sets/codeblock/setup.typoscript similarity index 98% rename from Configuration/TypoScript/setup.typoscript rename to Configuration/Sets/codeblock/setup.typoscript index d51e477..c6b0933 100644 --- a/Configuration/TypoScript/setup.typoscript +++ b/Configuration/Sets/codeblock/setup.typoscript @@ -1,7 +1,6 @@ tt_content.codeblock =< lib.contentElement tt_content.codeblock { templateName = Codeblock - templateRootPaths.0 = EXT:codeblock/Resources/Private/Templates dataProcessing.1567071612 = B13\Codeblock\DataProcessing\HighlightProcessor diff --git a/Configuration/TCA/Overrides/tt_content.php b/Configuration/TCA/Overrides/tt_content.php index 017cc3e..70ad10b 100644 --- a/Configuration/TCA/Overrides/tt_content.php +++ b/Configuration/TCA/Overrides/tt_content.php @@ -1,82 +1,77 @@ getMajorVersion() < 12) { - $CTypeSelectItem = [ - 'LLL:EXT:codeblock/Resources/Private/Language/locallang_db.xlf:tt_content.CType', - 'codeblock', - 'content-codeblock' - ]; - } else { - $CTypeSelectItem = [ - 'label' => 'LLL:EXT:codeblock/Resources/Private/Language/locallang_db.xlf:tt_content.CType', - 'value' => 'codeblock', - 'icon' => 'content-codeblock' - ]; - } - \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTcaSelectItem( - 'tt_content', - 'CType', - $CTypeSelectItem, - 'html', - 'after' - ); +ExtensionManagementUtility::addTcaSelectItem( + 'tt_content', + 'CType', + [ + 'label' => 'LLL:EXT:codeblock/Resources/Private/Language/locallang_db.xlf:tt_content.CType', + 'value' => 'codeblock', + 'icon' => 'content-codeblock', + 'group' => 'default', + 'description' => 'LLL:EXT:codeblock/Resources/Private/Language/locallang_db.xlf:tt_content.wizard.description', + ], + 'html', + 'after' +); - $GLOBALS['TCA']['tt_content']['ctrl']['typeicon_classes']['codeblock'] = 'content-codeblock'; +$GLOBALS['TCA']['tt_content']['ctrl']['typeicon_classes']['codeblock'] = 'content-codeblock'; - $GLOBALS['TCA']['tt_content']['types']['codeblock'] = [ - 'showitem' => ' - --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:general, - --palette--;;general, - --palette--;;headers, - bodytext;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:bodytext_formlabel, - --div--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:tabs.appearance, - --palette--;;frames, - --palette--;;appearanceLinks, - --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:language, - --palette--;;language, - --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access, - --palette--;;hidden, - --palette--;;access, - --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:categories, - categories, - --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:notes, - rowDescription, - --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:extended, - ', - 'columnsOverrides' => [ - 'bodytext' => [ - 'config' => [ - 'fixedFont' => true, - ], +$GLOBALS['TCA']['tt_content']['types']['codeblock'] = [ + 'previewRenderer' => ContentPreviewRenderer::class, + 'showitem' => ' + --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:general, + --palette--;;general, + --palette--;;headers, + bodytext;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:bodytext_formlabel, + --div--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:tabs.appearance, + --palette--;;frames, + --palette--;;appearanceLinks, + --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:language, + --palette--;;language, + --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access, + --palette--;;hidden, + --palette--;;access, + --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:categories, + categories, + --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:notes, + rowDescription, + --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:extended, + ', + 'columnsOverrides' => [ + 'bodytext' => [ + 'config' => [ + 'fixedFont' => true, ], ], - ]; + ], +]; - $additionalColumns = [ +ExtensionManagementUtility::addTCAcolumns( + 'tt_content', + [ 'code_language' => [ 'label' => 'LLL:EXT:codeblock/Resources/Private/Language/locallang_db.xlf:tt_content.code_language', 'config' => [ 'type' => 'select', - 'default' => '', - 'itemsProcFunc' => \B13\Codeblock\DataProvider\CodeLanguages::class . '->getAll', 'renderType' => 'selectSingle', + 'default' => '', + 'itemsProcFunc' => CodeLanguages::class . '->getAll', ], ], - ]; - - \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTCAcolumns('tt_content', $additionalColumns); - - \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addToAllTCAtypes( - 'tt_content', - 'code_language', - 'codeblock', - 'before:bodytext' - ); + ] +); - // for fluidBasedPageModule enabled (always for TYPO3 > 11) - $GLOBALS['TCA']['tt_content']['types']['codeblock']['previewRenderer'] = \B13\Codeblock\Backend\Preview\ContentPreviewRenderer::class; -}); +ExtensionManagementUtility::addToAllTCAtypes( + 'tt_content', + 'code_language', + 'codeblock', + 'before:bodytext' +); diff --git a/README.md b/README.md index d7ce5ad..1f308c8 100644 --- a/README.md +++ b/README.md @@ -1,141 +1,93 @@ # Code Block -## What does this extension do? +A TYPO3 content element for displaying source code with server-side syntax +highlighting via [highlight.php](https://github.com/scrivo/highlight.php). +The rendered output is cached with the content element, so no JavaScript runs +in the browser to colour the code. -Code Block is a TYPO3 extension. It adds a content type to display source code -processed using `highlight.php` to render code snippets with syntax highlighting. -The CSS-classes applied are identical to what highlight.js would render, but the -transformation takes place on the server (instead of the browser when using JS). +We use this extension to render the code snippets on our own blog at +[b13.com](https://b13.com). -The rendered result is cached like any other content element with the page in -TYPO3. Using this extension you can skip adding highlight.js to your JS-build. -This helps reduce the JavaScript size for your website and also allows rendering -of source code snippets for AMP pages for example. +## Requirements -## Code Languages +| Code Block | TYPO3 | PHP | +| ---------- | ---------- | ----- | +| 3.x | 13.4, 14.x | 8.2+ | +| 2.x | 10.4 – 14 | 7.4 – 8.x | -The extension supports all code languages that highlight.php supports. These can -either be specified by choosing a setting inside the content element or -detected automatically. +Version 3 drops support for TYPO3 11 and 12 and is delivered as a Site Set. +If you are on an older TYPO3, stay on the 2.x branch. ## Installation -Add this extension to your TYPO3 setup. Install using composer: `composer req b13/codeblock`. +Install via Composer: -Add the TypoScript to your site extensions setup: +``` +composer require b13/codeblock +``` -`@import 'EXT:codeblock/Configuration/TypoScript/setup.typoscript'` +Assign the **`b13/codeblock` Site Set** to your site configuration (Sites +module → Edit site → tab *Sets for this Site*). That registers the content +element, its wizard entry, and the TypoScript rendering. No `@import` of +TypoScript or PageTSconfig from your site extension is needed. -Add the PageTS (for adding the element to the New Content Element Wizard): +If you want to ship the Set automatically with your own site set, depend on +it from your site set's `config.yaml`: -`@import 'EXT:codeblock/Configuration/PageTs/PageTs.tsconfig'` +```yaml +dependencies: + - b13/codeblock +``` -If you want to use your own Fluid Template, add the Template Root Path to the setup like this: +## Using your own Fluid template -`tt_content.codeblock.templateRootPaths.10 = EXT:your_site_extension/Resources/Private/Contenttypes/Templates` +Add a higher-priority template root path in your site set's TypoScript: -### A note for Integrators: -If your Fluid Layout "Default" uses `` you should use a custom content type Fluid Template to avoid having -your frontend tabs/spaces missing for some parts. `Spacelesse` removes spaces between tags, and `highlight.php` can add -a series of `foo bar` strings that need the spaces between the tags to be readable and make -sense. +```typoscript +tt_content.codeblock.templateRootPaths.10 = EXT:your_site/Resources/Private/ContentElements/Codeblock/Templates +``` -## Styles +The template receives `bodytext_formatted.code`, `bodytext_formatted.language`, +and `bodytext_formatted.lines`. -CSS styling needs to be included manually. The classes added to the HTML output -are generated automatically. Their styling need to be specified in a CSS file -in order to add a custom styling. E.g. for JetBrain's darcula theme: +### Heads up: whitespace and `` -```css -.hljs { - display: block; - overflow-x: auto; - padding: 0.5em; - background: #2b2b2b; -} - -.hljs { - color: #bababa; -} - -.hljs-strong, -.hljs-emphasis { - color: #a8a8a2; -} - -.hljs-bullet, -.hljs-quote, -.hljs-link, -.hljs-number, -.hljs-regexp, -.hljs-literal { - color: #6896ba; -} - -.hljs-code, -.hljs-selector-class { - color: #a6e22e; -} - -.hljs-emphasis { - font-style: italic; -} +`highlight.php` emits markup like `foo bar` where +the spaces between tags are part of the rendered code. If your `Default` +Fluid layout wraps everything in ``, those spaces collapse and +the output gets unreadable. Either drop `` for this element or +ship a dedicated Fluid layout for `codeblock`. -.hljs-keyword, -.hljs-selector-tag, -.hljs-section, -.hljs-attribute, -.hljs-name, -.hljs-variable { - color: #cb7832; -} - -.hljs-params { - color: #b9b9b9; -} - -.hljs-string { - color: #6a8759; -} - -.hljs-subst, -.hljs-type, -.hljs-built_in, -.hljs-builtin-name, -.hljs-symbol, -.hljs-selector-id, -.hljs-selector-attr, -.hljs-selector-pseudo, -.hljs-template-tag, -.hljs-template-variable, -.hljs-addition { - color: #e0c46c; -} +## Styles + +The HTML classes match what highlight.js produces, so any +[highlight.js stylesheet](https://github.com/scrivo/highlight.php/tree/master/styles) +works. Include the stylesheet of your choice from your own site extension — +the extension itself ships no CSS. A minimal dark-on-dark example: +```css +.hljs { display: block; overflow-x: auto; padding: 0.5em; + background: #2b2b2b; color: #bababa; } .hljs-comment, .hljs-deletion, -.hljs-meta { - color: #7f7f7f; -} +.hljs-meta { color: #7f7f7f; } +.hljs-keyword, +.hljs-selector-tag { color: #cb7832; } +.hljs-string { color: #6a8759; } ``` -This extension uses `highlight.php` (see https://github.com/scrivo/highlight.php). -This package includes [a lot of different CSS style themes](https://github.com/scrivo/highlight.php/tree/master/styles) you can use. - ## License -As TYPO3 Core, _codeblock_ is licensed under GPL2 or later. See the LICENSE file for more details. +GPL-2.0-or-later, in line with TYPO3 Core. See `LICENSE`. -## Background, Authors & Further Maintenance +## Background -TYPO3 is highly configurable and it is easy to add custom content types to the system using a few lines of TCA -configuration, a simple PageTS configuration to add the type to the list of elements in the New Content Element Wizard, -and a few lines of TypoScript and a Fluid Template. -This extension adds a content type in the same way we create custom content types for our TYPO3 projects at -[b13](https://b13.com). +`EXT:codeblock` was originally written by Andreas Hämmerl and David Steeb in +2019 for [b13, Stuttgart](https://b13.com). The walk-through of how this +extension is built — and why we still ship "core-near" content elements +instead of relying on third-party builders — is on our blog: -`EXT:codeblock` was initially created by Andreas Hämmerl and David Steeb in 2019 for [b13, Stuttgart](https://b13.com). We -use it to display source code in our blog on [b13.com](https://b13.com), where we have a full-AMP website and do not -include non-AMP JavaScript files. +- [How to create custom content elements in TYPO3 (v14)](https://b13.com/blog) — *upcoming* +- [How to Create Custom Content Elements in TYPO3](https://b13.com/blog/how-to-create-custom-content-elements-in-typo3) — *2020, kept for reference* -[Find more TYPO3 extensions we have developed](https://b13.com/useful-typo3-extensions-from-b13-to-you) that help us deliver value in client projects. As part of the way we work, we focus on testing and best practices to ensure long-term performance, reliability, and results in all our code. +[More TYPO3 extensions we maintain](https://b13.com/useful-typo3-extensions-from-b13-to-you). diff --git a/composer.json b/composer.json index 19f57ee..57e1f6d 100644 --- a/composer.json +++ b/composer.json @@ -1,13 +1,21 @@ { "name": "b13/codeblock", "type": "typo3-cms-extension", - "description": "Custom Content Type for code snippet elements to show text with code highlighting in TYPO3.", + "description": "Custom content type for displaying source code with syntax highlighting in TYPO3.", "homepage": "https://github.com/b13/codeblock", "license": ["GPL-2.0-or-later"], "require": { - "scrivo/highlight.php": "^9.15", - "php": "^7.4 || ~8.0", - "typo3/cms-core": "^10.4 || ^11.5 || ^12.0 || ^13.0 || ^14.0" + "php": "^8.2", + "typo3/cms-core": "^13.4 || ^14.0", + "scrivo/highlight.php": "^9.15" + }, + "require-dev": { + "typo3/tailor": "^1.5" + }, + "autoload": { + "psr-4": { + "B13\\Codeblock\\": "Classes/" + } }, "extra": { "typo3/cms": { @@ -16,13 +24,5 @@ }, "support": { "issues": "https://github.com/b13/codeblock/issues" - }, - "autoload": { - "psr-4": { - "B13\\Codeblock\\": "Classes/" - } - }, - "require-dev": { - "typo3/tailor": "^1.2" } } diff --git a/ext_emconf.php b/ext_emconf.php index 5e46417..540a19c 100644 --- a/ext_emconf.php +++ b/ext_emconf.php @@ -9,10 +9,11 @@ 'author_company' => 'b13 GmbH', 'state' => 'stable', 'clearCacheOnLoad' => true, - 'version' => '2.1.0', + 'version' => '3.0.0', 'constraints' => [ 'depends' => [ - 'typo3' => '10.4.0-14.99.99', + 'typo3' => '13.4.0-14.99.99', + 'php' => '8.2.0-8.99.99', ], ], ]; diff --git a/ext_localconf.php b/ext_localconf.php deleted file mode 100644 index 4b30cf3..0000000 --- a/ext_localconf.php +++ /dev/null @@ -1,19 +0,0 @@ -getMajorVersion() < 12) { - $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/class.tx_cms_layout.php']['tt_content_drawItem']['codeblock'] = - \B13\Codeblock\Hooks\CodeblockPreviewRenderer::class; - - $iconRegistry = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Imaging\IconRegistry::class); - $iconRegistry->registerIcon( - 'content-codeblock', - \TYPO3\CMS\Core\Imaging\IconProvider\SvgIconProvider::class, - [ - 'source' => 'EXT:codeblock/Resources/Public/Icons/content-codeblock.svg', - ] - ); - } -}); diff --git a/ext_tables.sql b/ext_tables.sql deleted file mode 100644 index 009543f..0000000 --- a/ext_tables.sql +++ /dev/null @@ -1,3 +0,0 @@ -CREATE TABLE tt_content ( - code_language text -); From c0226d8aff5858e83f8f1f6da610f2950dd72f80 Mon Sep 17 00:00:00 2001 From: David Steeb Date: Fri, 22 May 2026 17:18:05 +0000 Subject: [PATCH 2/5] [TASK] Use addRecordType() for CType registration Replace the legacy addTcaSelectItem() + manual typeicon_classes + manual $GLOBALS['TCA']['tt_content']['types']['codeblock'] assignment + page.tsconfig wizard registration with a single ExtensionManagementUtility::addRecordType() call. addRecordType() (TYPO3 v13+) is the official convenience method that supersedes addPlugin() and addTcaSelectItem() for CType registration. It registers the select item, the typeicon, the wizard entry (group, title, description, icon) and the type's showitem / columnsOverrides / previewRenderer in one call, and automatically appends the 'extended' tab to the showitem string. The wizard registration in Configuration/Sets/codeblock/page.tsconfig is now redundant and removed. The code_language column is inlined in the type-specific showitem instead of being injected via addToAllTCAtypes(). --- Configuration/Sets/codeblock/page.tsconfig | 11 ---- Configuration/TCA/Overrides/tt_content.php | 77 ++++++++++------------ 2 files changed, 34 insertions(+), 54 deletions(-) delete mode 100644 Configuration/Sets/codeblock/page.tsconfig diff --git a/Configuration/Sets/codeblock/page.tsconfig b/Configuration/Sets/codeblock/page.tsconfig deleted file mode 100644 index 891ef73..0000000 --- a/Configuration/Sets/codeblock/page.tsconfig +++ /dev/null @@ -1,11 +0,0 @@ -mod.wizards.newContentElement { - wizardItems { - default.elements.codeblock { - iconIdentifier = content-codeblock - title = LLL:EXT:codeblock/Resources/Private/Language/locallang_db.xlf:tt_content.CType - description = LLL:EXT:codeblock/Resources/Private/Language/locallang_db.xlf:tt_content.wizard.description - tt_content_defValues.CType = codeblock - } - default.show := addToList(codeblock) - } -} diff --git a/Configuration/TCA/Overrides/tt_content.php b/Configuration/TCA/Overrides/tt_content.php index 70ad10b..1299e77 100644 --- a/Configuration/TCA/Overrides/tt_content.php +++ b/Configuration/TCA/Overrides/tt_content.php @@ -8,51 +8,49 @@ use B13\Codeblock\DataProvider\CodeLanguages; use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; -ExtensionManagementUtility::addTcaSelectItem( - 'tt_content', - 'CType', +// Register the CType, its showitem layout, the typeicon and the wizard entry +// in a single call. addRecordType() supersedes the older combination of +// addPlugin() / addTcaSelectItem() plus a manual $GLOBALS['TCA'][...]['types'] +// assignment. The "extended" tab is appended automatically. +ExtensionManagementUtility::addRecordType( [ 'label' => 'LLL:EXT:codeblock/Resources/Private/Language/locallang_db.xlf:tt_content.CType', + 'description' => 'LLL:EXT:codeblock/Resources/Private/Language/locallang_db.xlf:tt_content.wizard.description', 'value' => 'codeblock', 'icon' => 'content-codeblock', 'group' => 'default', - 'description' => 'LLL:EXT:codeblock/Resources/Private/Language/locallang_db.xlf:tt_content.wizard.description', ], - 'html', - 'after' -); - -$GLOBALS['TCA']['tt_content']['ctrl']['typeicon_classes']['codeblock'] = 'content-codeblock'; - -$GLOBALS['TCA']['tt_content']['types']['codeblock'] = [ - 'previewRenderer' => ContentPreviewRenderer::class, - 'showitem' => ' - --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:general, - --palette--;;general, - --palette--;;headers, - bodytext;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:bodytext_formlabel, - --div--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:tabs.appearance, - --palette--;;frames, - --palette--;;appearanceLinks, - --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:language, - --palette--;;language, - --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access, - --palette--;;hidden, - --palette--;;access, - --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:categories, - categories, - --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:notes, - rowDescription, - --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:extended, - ', - 'columnsOverrides' => [ - 'bodytext' => [ - 'config' => [ - 'fixedFont' => true, + ' + --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:general, + --palette--;;general, + --palette--;;headers, + code_language, + bodytext;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:bodytext_formlabel, + --div--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:tabs.appearance, + --palette--;;frames, + --palette--;;appearanceLinks, + --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:language, + --palette--;;language, + --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access, + --palette--;;hidden, + --palette--;;access, + --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:categories, + categories, + --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:notes, + rowDescription, + ', + [ + 'previewRenderer' => ContentPreviewRenderer::class, + 'columnsOverrides' => [ + 'bodytext' => [ + 'config' => [ + 'fixedFont' => true, + ], ], ], ], -]; + 'after:html' +); ExtensionManagementUtility::addTCAcolumns( 'tt_content', @@ -68,10 +66,3 @@ ], ] ); - -ExtensionManagementUtility::addToAllTCAtypes( - 'tt_content', - 'code_language', - 'codeblock', - 'before:bodytext' -); From d06579d61b2d0d986abb67a119af68c28f7b751f Mon Sep 17 00:00:00 2001 From: David Steeb Date: Fri, 22 May 2026 21:13:21 +0200 Subject: [PATCH 3/5] [TASK] Declare cms-backend/cms-frontend deps and Set description The extension uses classes from typo3/cms-backend (StandardContentPreviewRenderer, GridColumnItem) and typo3/cms-frontend (ContentObjectRenderer, DataProcessorInterface), but composer.json only declared typo3/cms-core. Add both as direct dependencies so slimmed-down installs resolve correctly and the dependency graph reflects actual usage. Also adds a 'description' field to the Site Set config so editors see what the Set provides in the backend "Sets for this Site" UI. --- Configuration/Sets/codeblock/config.yaml | 1 + composer.json | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Configuration/Sets/codeblock/config.yaml b/Configuration/Sets/codeblock/config.yaml index 5aa9c4d..0365b87 100644 --- a/Configuration/Sets/codeblock/config.yaml +++ b/Configuration/Sets/codeblock/config.yaml @@ -1,4 +1,5 @@ name: b13/codeblock label: 'b13: Code Block content element' +description: 'Adds a "Code Block" content type for syntax-highlighted source snippets.' dependencies: - typo3/fluid-styled-content diff --git a/composer.json b/composer.json index 57e1f6d..1e1ff0c 100644 --- a/composer.json +++ b/composer.json @@ -7,6 +7,8 @@ "require": { "php": "^8.2", "typo3/cms-core": "^13.4 || ^14.0", + "typo3/cms-backend": "^13.4 || ^14.0", + "typo3/cms-frontend": "^13.4 || ^14.0", "scrivo/highlight.php": "^9.15" }, "require-dev": { From 3fe1d668b7b9f27fd7a47a5a6e185df4d030748b Mon Sep 17 00:00:00 2001 From: David Steeb Date: Fri, 22 May 2026 21:23:20 +0200 Subject: [PATCH 4/5] [TASK] Remove deprecated clearCacheOnLoad from ext_emconf The clearCacheOnLoad key has been a no-op since TYPO3 v11 and was dropped from the documented ext_emconf schema in v13. The rest of the extension was already clean for v13/v14 (see CHANGELOG 3.0.0). --- ext_emconf.php | 1 - 1 file changed, 1 deletion(-) diff --git a/ext_emconf.php b/ext_emconf.php index 540a19c..b2e6b0d 100644 --- a/ext_emconf.php +++ b/ext_emconf.php @@ -8,7 +8,6 @@ 'author_email' => 'typo3@b13.com', 'author_company' => 'b13 GmbH', 'state' => 'stable', - 'clearCacheOnLoad' => true, 'version' => '3.0.0', 'constraints' => [ 'depends' => [ From 6a7eb3f8c481c9df9b00f41e6893a7442543069a Mon Sep 17 00:00:00 2001 From: David Steeb Date: Sun, 24 May 2026 16:38:44 +0000 Subject: [PATCH 5/5] =?UTF-8?q?[TASK]=20Shrink=20showitem=20=E2=80=94=20sy?= =?UTF-8?q?stem=20tabs=20auto-added=20since=20v13.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Feature #104814 (TYPO3 v13.3) adds the General, Language, Access, Notes and Extended tabs (plus their underlying system palettes) to every CType automatically, based on the table's ctrl section. With the extension already requiring ^13.4 || ^14.0 we can rely on this and only declare element-specific fields and the non-system tabs we actually use. --- Configuration/TCA/Overrides/tt_content.php | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/Configuration/TCA/Overrides/tt_content.php b/Configuration/TCA/Overrides/tt_content.php index 1299e77..fc2479e 100644 --- a/Configuration/TCA/Overrides/tt_content.php +++ b/Configuration/TCA/Overrides/tt_content.php @@ -11,7 +11,10 @@ // Register the CType, its showitem layout, the typeicon and the wizard entry // in a single call. addRecordType() supersedes the older combination of // addPlugin() / addTcaSelectItem() plus a manual $GLOBALS['TCA'][...]['types'] -// assignment. The "extended" tab is appended automatically. +// assignment. The General, Language, Access, Notes and Extended tabs (plus +// their underlying system palettes) are added automatically based on the +// ctrl section since v13.3 (Feature #104814) — we only declare element- +// specific fields and any non-system tabs we actually want. ExtensionManagementUtility::addRecordType( [ 'label' => 'LLL:EXT:codeblock/Resources/Private/Language/locallang_db.xlf:tt_content.CType', @@ -21,23 +24,14 @@ 'group' => 'default', ], ' - --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:general, - --palette--;;general, - --palette--;;headers, - code_language, - bodytext;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:bodytext_formlabel, + --palette--;;headers, + code_language, + bodytext;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:bodytext_formlabel, --div--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:tabs.appearance, --palette--;;frames, --palette--;;appearanceLinks, - --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:language, - --palette--;;language, - --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access, - --palette--;;hidden, - --palette--;;access, --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:categories, categories, - --div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:notes, - rowDescription, ', [ 'previewRenderer' => ContentPreviewRenderer::class,