Skip to content

Commit

Permalink
[BETA][FEATURE] Add TYPO3 v13 compatibility (#778)
Browse files Browse the repository at this point in the history
* [FEATURE] Add TYPO3 v13 compatibility
- adjust tests for T3v13

* [TASK] Improve tests for v13 LTS & PHPUnit v11

* [BUGFIX] Generate proper frontend url for hreflangs

* [TASK] Introduce HeadlessVersion
TYPO3's Core inspired simple utility to provide small helper to tackle code adjusting for different versions of ext:headless

* [TASK] Set version 4.5.0

* [TASK] Adjusting README

* [BUGFIX] Call parent properly in CLI context

* [BUGFIX] Fix potential warnings

* [BUGFIX] Do not try to import on v12 not existing class
  • Loading branch information
twoldanski authored Oct 28, 2024
1 parent e87413b commit 3a55bfc
Show file tree
Hide file tree
Showing 72 changed files with 706 additions and 1,472 deletions.
74 changes: 69 additions & 5 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,69 @@ jobs:
- "php:cs-fixer"
php-version:
- 8.2
tests:
testV12:
runs-on: ubuntu-latest
strategy:
max-parallel: 2
matrix:
php-versions: [8.1]
php-versions: [ 8.1, 8.2 ]
typo3-versions:
- {typo3: 12, testing: ^7.0@dev}
- { typo3: 12, testing: ^8.0, phpunit: ^10, phpcov: ^9, yaml: ^6 }

name: "Run tests with PHP ${{ matrix.php-versions }}
using TYPO3 ${{ matrix.typo3-versions.typo3 }}
with testing framework version ${{ matrix.typo3-versions.testing }}"
steps:
- uses: actions/checkout@v4

- uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
extensions: intl, mbstring, pdo_sqlite

- name: "Cache composer dependencies"
uses: actions/cache@v4
with:
path: ~/.composer/cache
key: php-${{ matrix.php-versions }}
-typo3-${{ matrix.typo3-versions.typo3 }}
-phpunit-${{ matrix.typo3-versions.phpunit }}
-phpcov-${{ matrix.typo3-versions.phpcov }}
-yaml-${{ matrix.typo3-versions.yaml }}
-testing-${{ matrix.typo3-versions.testing }}
-composer-${{ hashFiles('composer.json') }}
restore-keys: |
php-${{ matrix.php-versions }}
-typo3-${{ matrix.typo3-versions.typo3 }}
-phpunit-${{ matrix.typo3-versions.phpunit }}
-phpcov-${{ matrix.typo3-versions.phpcov }}
-yaml-${{ matrix.typo3-versions.yaml }}
-testing-${{ matrix.typo3-versions.testing }}composer-
php-${{ matrix.php-versions }}-typo3-
- name: "Install composer dependencies"
run: composer require typo3/minimal
"^${{ matrix.typo3-versions.typo3 }}"
typo3/testing-framework "${{ matrix.typo3-versions.testing }}"
phpunit/phpunit "${{ matrix.typo3-versions.phpunit }}"
phpunit/phpcov "${{ matrix.typo3-versions.phpcov }}"
symfony/yaml "${{ matrix.typo3-versions.yaml }}"
--prefer-dist --no-progress

- name: "Run Unit tests"
run: composer ci:test:unit

- name: "Functional tests"
run: composer ci:test:functional

testsV13:
runs-on: ubuntu-latest
strategy:
max-parallel: 2
matrix:
php-versions: [8.2, 8.3]
typo3-versions:
- {typo3: 13, testing: ^9.0, phpunit: ^11, phpcov: ^10, yaml: ^7 }

name: "Run tests with PHP ${{ matrix.php-versions }}
using TYPO3 ${{ matrix.typo3-versions.typo3 }}
Expand All @@ -89,19 +144,28 @@ jobs:
path: ~/.composer/cache
key: php-${{ matrix.php-versions }}
-typo3-${{ matrix.typo3-versions.typo3 }}
-phpunit-${{ matrix.typo3-versions.phpunit }}
-phpcov-${{ matrix.typo3-versions.phpcov }}
-yaml-${{ matrix.typo3-versions.yaml }}
-testing-${{ matrix.typo3-versions.testing }}
-composer-${{ hashFiles('composer.json') }}
restore-keys: |
php-${{ matrix.php-versions }}
-typo3-${{ matrix.typo3-versions.typo3 }}
-phpunit-${{ matrix.typo3-versions.phpunit }}
-phpcov-${{ matrix.typo3-versions.phpcov }}
-yaml-${{ matrix.typo3-versions.yaml }}
-testing-${{ matrix.typo3-versions.testing }}composer-
php-${{ matrix.php-versions }}-typo3-
- name: "Install composer dependencies"
run: composer require typo3/minimal
run: composer remove php-coveralls/php-coveralls --dev --no-progress && composer require typo3/minimal
"^${{ matrix.typo3-versions.typo3 }}"
typo3/testing-framework "${{ matrix.typo3-versions.testing }}"
--prefer-dist --no-progress --no-suggest
phpunit/phpunit "${{ matrix.typo3-versions.phpunit }}"
phpunit/phpcov "${{ matrix.typo3-versions.phpcov }}"
symfony/yaml "${{ matrix.typo3-versions.yaml }}"
--prefer-dist --no-progress

- name: "Run Unit tests"
run: composer ci:test:unit
Expand Down
53 changes: 46 additions & 7 deletions Classes/ContentObject/JsonContentContentObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
use FriendsOfTYPO3\Headless\Json\JsonEncoder;
use FriendsOfTYPO3\Headless\Json\JsonEncoderInterface;
use FriendsOfTYPO3\Headless\Utility\HeadlessUserInt;
use Psr\EventDispatcher\EventDispatcherInterface;
use TYPO3\CMS\Backend\View\BackendLayoutView;
use TYPO3\CMS\Core\Information\Typo3Version;
use TYPO3\CMS\Core\TimeTracker\TimeTracker;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Frontend\ContentObject\ContentContentObject;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
Expand All @@ -23,6 +26,7 @@
use function count;
use function is_array;
use function json_decode;
use function json_encode;
use function str_contains;
use function trim;

Expand Down Expand Up @@ -97,16 +101,25 @@
* }
* returnSingleRow = 1
* }
*
* @codeCoverageIgnore
*/
class JsonContentContentObject extends ContentContentObject
{
private HeadlessUserInt $headlessUserInt;
private JsonEncoderInterface $jsonEncoder;
/**
* @var mixed|object|\Psr\Log\LoggerAwareInterface|\TYPO3\CMS\Core\SingletonInterface|TimeTracker|(TimeTracker&\Psr\Log\LoggerAwareInterface)|(TimeTracker&\TYPO3\CMS\Core\SingletonInterface)|null
*/
private TimeTracker $timeTracker;
private EventDispatcherInterface $eventDispatcher;

public function __construct()
{
$this->headlessUserInt = GeneralUtility::makeInstance(HeadlessUserInt::class);
$this->jsonEncoder = GeneralUtility::makeInstance(JsonEncoder::class);
$this->timeTracker = GeneralUtility::makeInstance(TimeTracker::class);
$this->eventDispatcher = GeneralUtility::makeInstance(EventDispatcherInterface::class);
}

/**
Expand Down Expand Up @@ -199,6 +212,8 @@ protected function groupContentElementsByColPos(array $contentElements, array $c
*/
private function prepareValue(array $conf): array
{
$t3v13andAbove = (new Typo3Version())->getMajorVersion() >= 13;

$frontendController = $this->getTypoScriptFrontendController();
$theValue = [];
$originalRec = $frontendController->currentRecord;
Expand Down Expand Up @@ -233,20 +248,44 @@ private function prepareValue(array $conf): array
$tmpValue = '';

do {
$records = $this->cObj->getRecords($conf['table'], $conf['select.']);
if ($t3v13andAbove) {
$modifyRecordsEvent = $this->eventDispatcher->dispatch(
new \TYPO3\CMS\Frontend\ContentObject\Event\ModifyRecordsAfterFetchingContentEvent(
$this->cObj->getRecords($conf['table'], $conf['select.']),
json_encode($theValue, JSON_THROW_ON_ERROR),
$slide,
$slideCollect,
$slideCollectReverse,
$slideCollectFuzzy,
$conf
)
);

$records = $modifyRecordsEvent->getRecords();
$theValue = json_decode($modifyRecordsEvent->getFinalContent(), true, 512, JSON_THROW_ON_ERROR);
$slide = $modifyRecordsEvent->getSlide();
$slideCollect = $modifyRecordsEvent->getSlideCollect();
$slideCollectReverse = $modifyRecordsEvent->getSlideCollectReverse();
$slideCollectFuzzy = $modifyRecordsEvent->getSlideCollectFuzzy();
$conf = $modifyRecordsEvent->getConfiguration();
} else {
$records = $this->cObj->getRecords($conf['table'], $conf['select.']);
}
$cobjValue = [];
if (!empty($records)) {
$this->getTimeTracker()->setTSlogMessage('NUMROWS: ' . count($records));
$this->timeTracker->setTSlogMessage('NUMROWS: ' . count($records));

$cObj = GeneralUtility::makeInstance(ContentObjectRenderer::class, $frontendController);
$cObj->setParent($this->cObj->data, $this->cObj->currentRecord);
$this->cObj->currentRecordNumber = 0;

foreach ($records as $row) {
// Call hook for possible manipulation of database row for cObj->data
foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content_content.php']['modifyDBRow'] ?? [] as $className) {
$_procObj = GeneralUtility::makeInstance($className);
$_procObj->modifyDBRow($row, $conf['table']);
if (!$t3v13andAbove) {
// Call hook for possible manipulation of database row for cObj->data
foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content_content.php']['modifyDBRow'] ?? [] as $className) {
$_procObj = GeneralUtility::makeInstance($className);
$_procObj->modifyDBRow($row, $conf['table']);
}
}
$registerField = $conf['table'] . ':' . ($row['uid'] ?? 0);
if (!($frontendController->recordRegister[$registerField] ?? false)) {
Expand Down Expand Up @@ -282,7 +321,7 @@ private function prepareValue(array $conf): array
}
$again = (string)$conf['select.']['pidInList'] !== '';
}
} while ($again && $slide && ((string)$tmpValue === '' && $slideCollectFuzzy || $slideCollect));
} while ($again && $slide && (((string)$tmpValue === '' && $slideCollectFuzzy) || $slideCollect));

$theValue = $this->groupContentElementsByColPos($theValue, $conf);
// Restore
Expand Down
6 changes: 5 additions & 1 deletion Classes/DataProcessing/FlexFormProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,11 @@ public function process(ContentObjectRenderer $cObj, array $contentObjectConfigu
}

// processing the flexform data
$originalValue = $processedData['data'][$fieldName] ?? $processedData[$fieldName];
$originalValue = $processedData['data'][$fieldName] ?? $processedData[$fieldName] ?? null;

if ($originalValue === null || $originalValue === '' || $originalValue === []) {
return $processedData;
}

if (is_array($originalValue)) {
$flexformData = $originalValue;
Expand Down
34 changes: 34 additions & 0 deletions Classes/Event/Listener/HeadlessHreflangGeneratorListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

/*
* This file is part of the "headless" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.md file that was distributed with this source code.
*/

declare(strict_types=1);

namespace FriendsOfTYPO3\Headless\Event\Listener;

use FriendsOfTYPO3\Headless\Utility\UrlUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Frontend\Event\ModifyHrefLangTagsEvent;

/**
* @codeCoverageIgnore
*/
class HeadlessHreflangGeneratorListener
{
public function __invoke(ModifyHrefLangTagsEvent $event): void
{
$hrefLangs = [];
$urlUtility = GeneralUtility::makeInstance(UrlUtility::class)->withRequest($event->getRequest());

foreach ($event->getHrefLangs() as $lang => $hrefLang) {
$hrefLangs[$lang] = $urlUtility->getFrontendUrlWithSite($hrefLang, $event->getRequest()->getAttribute('site'));
}

$event->setHrefLangs($hrefLangs);
}
}
Loading

0 comments on commit 3a55bfc

Please sign in to comment.