Skip to content

Commit

Permalink
feat: TYPO3 13.4 compatibility
Browse files Browse the repository at this point in the history
  • Loading branch information
saschanowak committed Nov 9, 2024
1 parent 3b493ed commit dc3b601
Show file tree
Hide file tree
Showing 9 changed files with 122 additions and 260 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/apply-coding-standard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
- name: 'Setup PHP'
uses: shivammathur/setup-php@v2
with:
php-version: '8.1'
php-version: '8.3'
tools: composer

- name: 'Get Composer Cache Directory'
Expand All @@ -26,7 +26,7 @@ jobs:
echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: 'Cache Composer Dependencies'
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: composer
Expand All @@ -53,7 +53,7 @@ jobs:

- name: 'Create pull-request'
id: cpr
uses: peter-evans/create-pull-request@v5.0.2
uses: peter-evans/create-pull-request@v6.0.3
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: "[automated] Apply Coding Standard"
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ permissions:
jobs:
test:
runs-on: ubuntu-latest
name: TYPO3 v12.4 tests on PHP ${{ matrix.php }}
name: TYPO3 v13.4 tests on PHP ${{ matrix.php }}

strategy:
fail-fast: false
matrix:
php: [ 8.1, 8.2, 8.3 ]
php: [ 8.3 ]

steps:
- name: 'Checkout code'
Expand All @@ -46,7 +46,7 @@ jobs:
echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: 'Cache Composer Dependencies'
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: composer-php${{ matrix.php }}
Expand All @@ -72,7 +72,7 @@ jobs:
run: .Build/bin/phpcov merge --html .Build/artifacts/coverage/merged --clover .Build/artifacts/coverage/clover.xml .Build/artifacts/coverage/

- name: 'Generate code coverage summary report'
uses: saschanowak/CloverCodeCoverageSummary@0.4.0
uses: saschanowak/CloverCodeCoverageSummary@1.0.1
with:
filename: .Build/artifacts/coverage/clover.xml

Expand Down
2 changes: 1 addition & 1 deletion Classes/Middleware/AcceptHeaderHashBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class AcceptHeaderHashBase implements MiddlewareInterface

public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$function = function (array &$params) use ($request) {
$function = function (array &$params) use ($request): void {
$params['hashParameters'][self::class] = [];
$params['hashParameters'][self::class]['REQUEST_METHOD'] = strtoupper((string) $request->getMethod());

Expand Down
64 changes: 24 additions & 40 deletions Classes/Middleware/AjaxRequestMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,12 @@
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use RuntimeException;
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Cache\CacheTag;
use TYPO3\CMS\Core\Http\Response;
use TYPO3\CMS\Core\Http\Stream;
use TYPO3\CMS\Core\Routing\PageArguments;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;

class AjaxRequestMiddleware implements MiddlewareInterface
{
Expand All @@ -28,60 +25,48 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface
$routeArguments = $routing->getRouteArguments();

if (
str_contains((string) $request->getHeaderLine('Accept'), 'application/json')
str_contains($request->getHeaderLine('Accept'), 'application/json')
&& (is_countable($routeArguments) ? count($routeArguments) : 0) > 0
) {
$controller = $request->getAttribute('frontend.controller');
assert($controller instanceof TypoScriptFrontendController);

$controller->preparePageContentGeneration($request);
if (!$controller->tmpl->setup) {
throw new RuntimeException(
'TypoScript not properly loaded, probably due to cached content.',
1_593_611_242
);
}

$pluginNamespace = array_key_first($routeArguments);
[$prefix, $extensionName, $pluginName] = explode('_', (string) $pluginNamespace);
$pluginSignature = strtolower($extensionName . '_' . $pluginName);
$typoScriptObjectPath = 'tt_content.list.20.' . $pluginSignature;

$setup = $this->getPluginTypoScriptConfiguration($controller, $typoScriptObjectPath);
$setup = $this->getPluginTypoScriptConfiguration($request, $typoScriptObjectPath);
$typoScriptObjectName = $setup[$pluginSignature];
$typoScriptObjectConfiguration = $setup[$pluginSignature . '.'];
$typoScriptObjectConfiguration['controller'] = $routeArguments[$pluginNamespace]['controller'] ?? '';
$typoScriptObjectConfiguration['action'] = $routeArguments[$pluginNamespace]['action'] ?? '';

$this->addPluginFormatToRequest($request, $pluginNamespace);
$request = $this->addPluginFormatToRequest($request, $pluginNamespace);

$contentContentObject = GeneralUtility::makeInstance(ContentObjectRenderer::class);
$contentContentObject->setUserObjectType(ContentObjectRenderer::OBJECTTYPE_USER);
$contentContentObject->setRequest($request);
$pageContent = $contentContentObject->cObjGetSingle(
$typoScriptObjectName,
$typoScriptObjectConfiguration,
$typoScriptObjectPath
);

$pageId = $request->getAttribute('frontend.page.information')->getId();
$pageCacheTag = new CacheTag('pageId_' . $pageId);
if (str_starts_with((string) $pageContent, '<!--INT_SCRIPT')) {
$contentContentObject = GeneralUtility::makeInstance(ContentObjectRenderer::class);
$contentContentObject->setRequest($request);
$contentContentObject->setUserObjectType(ContentObjectRenderer::OBJECTTYPE_USER_INT);
$pageContent = $contentContentObject->cObjGetSingle(
$typoScriptObjectName,
$typoScriptObjectConfiguration,
$typoScriptObjectPath
);
$controller->no_cache = true;
$request->getAttribute('frontend.cache.instruction')
->disableCache('EXT:nxajax: Caching disabled using uncached extbase plugin.');
$pageCacheTag = new CacheTag('pageId_' . $pageId, 0);
}

$controller->addCacheTags(['pageId_' . $controller->id]);
ObjectAccess::setProperty(
$controller,
'cacheExpires',
GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect(
'date',
'timestamp'
) + $controller->get_cache_timeout()
);
$request->getAttribute('frontend.cache.collector')
->addCacheTags($pageCacheTag);

$response = new Response();
$body = new Stream('php://temp', 'rw');
Expand All @@ -95,32 +80,32 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface
// @codeCoverageIgnoreEnd

$response = $response->withBody($body);
$response = $controller->applyHttpHeadersToResponse($response)
->withHeader('Content-Length', (string) strlen((string) $pageContent))
->withHeader('Content-Type', 'application/json;charset=utf-8');
} else {
$response = $handler->handle($request);
return $request->getAttribute('frontend.controller')
->applyHttpHeadersToResponse($request, $response)
->withHeader('Content-Length', (string) strlen((string) $pageContent));
}

return $response;
return $handler->handle($request);
}

private function getPluginTypoScriptConfiguration(
TypoScriptFrontendController $controller,
ServerRequestInterface $request,
string $typoScriptObjectPath
): array {
$pathSegments = GeneralUtility::trimExplode('.', $typoScriptObjectPath);
$lastSegment = array_pop($pathSegments);
$setup = $controller->tmpl->setup;
$setup = $request->getAttribute('frontend.typoscript')->getSetupArray();
foreach ($pathSegments as $segment) {
if (!array_key_exists($segment . '.', $setup)) {
throw new Exception(
'TypoScript object path "' . $typoScriptObjectPath . '" does not exist',
1_592_475_630
);
}

$setup = $setup[$segment . '.'];
}

if (!isset($setup[$lastSegment])) {
throw new Exception(
'No Content Object definition found at TypoScript object path "' . $typoScriptObjectPath . '"',
Expand All @@ -131,13 +116,13 @@ private function getPluginTypoScriptConfiguration(
return $setup;
}

private function addPluginFormatToRequest(ServerRequestInterface $request, string $pluginNamespace): void
private function addPluginFormatToRequest(ServerRequestInterface $request, string $pluginNamespace): ServerRequestInterface
{
$routeArguments = $request->getAttribute('routing');
assert($routeArguments instanceof PageArguments);
$arguments = $routeArguments->getArguments();
$arguments[$pluginNamespace]['format'] = 'json';
$request = $request->withAttribute(
return $request->withAttribute(
'routing',
new PageArguments(
$routeArguments->getPageId(),
Expand All @@ -147,6 +132,5 @@ private function addPluginFormatToRequest(ServerRequestInterface $request, strin
$routeArguments->getDynamicArguments(),
)
);
$GLOBALS['TYPO3_REQUEST'] = $request;
}
}
4 changes: 3 additions & 1 deletion Classes/Mvc/Controller/AjaxController.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

class AjaxController extends ExtbaseActionController
{
protected $defaultViewObjectName = JsonView::class;
protected ?string $defaultViewObjectName = JsonView::class;

public function errorAction(): ResponseInterface
{
Expand All @@ -23,6 +23,7 @@ public function errorAction(): ResponseInterface
if (!$validationResult->hasErrors()) {
continue;
}

$flattenErrors = $validationResult->getFlattenedErrors();
foreach ($flattenErrors as $fullQualifiedPropertyPath => $errors) {
[$propertyName] = explode('.', (string) $fullQualifiedPropertyPath, 2);
Expand All @@ -36,6 +37,7 @@ public function errorAction(): ResponseInterface
}
}
}

$this->view->assign('errors', [
'errors' => $result,
]);
Expand Down
43 changes: 21 additions & 22 deletions Classes/Mvc/View/JsonView.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,32 @@

class JsonView implements ViewInterface
{
protected $exposeSettings = false;
protected bool $exposeSettings = false;

protected $variables = [];
protected array $variables = [];

public function setExposeSettings(bool $exposeSettings)
public function setExposeSettings(bool $exposeSettings): void
{
$this->exposeSettings = $exposeSettings;
}

public function render()
public function assign($key, $value): static
{
$this->variables[$key] = $value;

return $this;
}

public function assignMultiple(array $values): static
{
foreach ($values as $key => $value) {
$this->assign($key, $value);
}

return $this;
}

public function render(): string
{
$variables = $this->variables;
if (!$this->exposeSettings) {
Expand All @@ -33,25 +49,8 @@ public function render()

if ((is_countable($variables) ? count($variables) : 0) === 1) {
return json_encode(current($variables), JSON_THROW_ON_ERROR);
} else {
return json_encode($variables, JSON_THROW_ON_ERROR);
}
}

public function assign($key, $value)
{
$this->variables[$key] = $value;

return $this;
}

public function assignMultiple(array $values)
{
foreach ($values as $key => $value) {
$this->assign($key, $value);
}

return $this;
return json_encode($variables, JSON_THROW_ON_ERROR);
}

public function renderSection($sectionName, array $variables = [], $ignoreUnknown = false)
Expand Down
Loading

0 comments on commit dc3b601

Please sign in to comment.