TYPO3 v15 dev-main snapshot ()
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Core\Context;
|
||||
|
||||
use TYPO3\CMS\Core\Context\Exception\AspectPropertyNotFoundException;
|
||||
|
||||
/**
|
||||
* Interface AspectInterface
|
||||
* Think of an aspect like a property bag
|
||||
*/
|
||||
interface AspectInterface
|
||||
{
|
||||
/**
|
||||
* Get a property from an aspect
|
||||
*
|
||||
* @return mixed
|
||||
* @throws AspectPropertyNotFoundException
|
||||
*/
|
||||
public function get(string $name);
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Core\Context;
|
||||
|
||||
use TYPO3\CMS\Core\Context\Exception\AspectNotFoundException;
|
||||
use TYPO3\CMS\Core\Context\Exception\AspectPropertyNotFoundException;
|
||||
use TYPO3\CMS\Core\Domain\DateTimeFactory;
|
||||
use TYPO3\CMS\Core\SingletonInterface;
|
||||
|
||||
/**
|
||||
* Contains the state of a current page request, to be used when reading
|
||||
* information of the current request in which configuration/context
|
||||
* it is used.
|
||||
*
|
||||
* Typically, the current main context is initialized very early within each entry-point application,
|
||||
* and is then modified overridden in e.g. PSR-15 middlewares (e.g. authentication, preview settings etc).
|
||||
*
|
||||
* For most use-cases, the current main context is fetched via GeneralUtility::makeInstance(Context::class),
|
||||
* however, if custom settings for a single use-case is necessary, it is recommended to clone the base context:
|
||||
*
|
||||
* ```
|
||||
* $mainContext = GeneralUtility::makeInstance(Context::class);
|
||||
* $customContext = clone $mainContext;
|
||||
* $customContext->setAspect(GeneralUtility::makeInstance(VisibilityAspect::class, true, true, false))
|
||||
* ```
|
||||
*
|
||||
* ... which in turn can be injected in the various places where TYPO3 uses contexts.
|
||||
*
|
||||
*
|
||||
* Classic aspect names to be used are:
|
||||
* - date (DateTimeAspect)
|
||||
* - workspace
|
||||
* - visibility
|
||||
* - frontend.user
|
||||
* - backend.user
|
||||
* - language
|
||||
* - frontend.preview [if EXT:frontend is loaded]
|
||||
*/
|
||||
class Context implements SingletonInterface
|
||||
{
|
||||
/**
|
||||
* @var AspectInterface[]
|
||||
*/
|
||||
protected array $aspects = [];
|
||||
|
||||
/**
|
||||
* Checks if an aspect exists in the context
|
||||
*/
|
||||
public function hasAspect(string $name): bool
|
||||
{
|
||||
return match ($name) {
|
||||
'date', 'visibility', 'backend.user', 'frontend.user', 'workspace', 'language' => true,
|
||||
default => isset($this->aspects[$name]),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an aspect, if it is set
|
||||
*
|
||||
* @throws AspectNotFoundException
|
||||
* @return ($name is 'date' ? DateTimeAspect
|
||||
* : ($name is 'visibility' ? VisibilityAspect
|
||||
* : ($name is 'backend.user' ? UserAspect
|
||||
* : ($name is 'frontend.user' ? UserAspect
|
||||
* : ($name is 'workspace' ? WorkspaceAspect
|
||||
* : ($name is 'language' ? LanguageAspect : AspectInterface))))))
|
||||
*/
|
||||
public function getAspect(string $name): AspectInterface
|
||||
{
|
||||
if (!isset($this->aspects[$name])) {
|
||||
// Ensure the default aspects are available, this is mostly necessary for tests to not set up everything
|
||||
switch ($name) {
|
||||
case 'date':
|
||||
$this->setAspect('date', new DateTimeAspect(DateTimeFactory::createFromTimestamp($GLOBALS['EXEC_TIME'])));
|
||||
break;
|
||||
case 'visibility':
|
||||
$this->setAspect('visibility', new VisibilityAspect());
|
||||
break;
|
||||
case 'backend.user':
|
||||
$this->setAspect('backend.user', new UserAspect());
|
||||
break;
|
||||
case 'frontend.user':
|
||||
$this->setAspect('frontend.user', new UserAspect());
|
||||
break;
|
||||
case 'workspace':
|
||||
$this->setAspect('workspace', new WorkspaceAspect());
|
||||
break;
|
||||
case 'language':
|
||||
$this->setAspect('language', new LanguageAspect());
|
||||
break;
|
||||
default:
|
||||
throw new AspectNotFoundException('No aspect named "' . $name . '" found.', 1527777641);
|
||||
}
|
||||
}
|
||||
return $this->aspects[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a property from the aspect, but only if the property is found.
|
||||
*
|
||||
* @throws AspectNotFoundException
|
||||
*/
|
||||
public function getPropertyFromAspect(string $name, string $property, mixed $default = null): mixed
|
||||
{
|
||||
if (!$this->hasAspect($name)) {
|
||||
throw new AspectNotFoundException('No aspect named "' . $name . '" found.', 1527777868);
|
||||
}
|
||||
try {
|
||||
return $this->getAspect($name)->get($property);
|
||||
} catch (AspectPropertyNotFoundException) {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an aspect, or overrides an existing aspect if an aspect is already set
|
||||
*/
|
||||
public function setAspect(string $name, AspectInterface $aspect): void
|
||||
{
|
||||
$this->aspects[$name] = $aspect;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Using this method is a sign of a technical debt. It is used by RedirectService,
|
||||
* but may vanish any time when this is fixed, and thus internal.
|
||||
* In general, Context aspects should never have to be unset.
|
||||
* When a middleware has to use this method, it is either located
|
||||
* at the wrong position in the chain, or has some other dependency issue.
|
||||
*/
|
||||
public function unsetAspect(string $name): void
|
||||
{
|
||||
unset($this->aspects[$name]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Core\Context;
|
||||
|
||||
use TYPO3\CMS\Core\Context\Exception\AspectPropertyNotFoundException;
|
||||
|
||||
/**
|
||||
* The Aspect is usually available as "date.*" properties in the Context.
|
||||
*
|
||||
* Contains the current time + date + timezone,
|
||||
* and needs a DateTimeImmutable object
|
||||
*
|
||||
* Allowed properties:
|
||||
* - timestamp - unix timestamp number
|
||||
* - timezone - America/Los_Angeles
|
||||
* - iso - datetime as string in ISO 8601 format, e.g. `2004-02-12T15:19:21+00:00`
|
||||
* - full - the DateTimeImmutable object
|
||||
* - accessTime - 60 seconds precision timestamp
|
||||
*/
|
||||
final readonly class DateTimeAspect implements AspectInterface
|
||||
{
|
||||
public function __construct(
|
||||
private \DateTimeImmutable $dateTimeObject,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Fetch a property of the date time object or the object itself ("full").
|
||||
*
|
||||
* @throws AspectPropertyNotFoundException
|
||||
*/
|
||||
public function get(string $name): \DateTimeImmutable|string|int
|
||||
{
|
||||
switch ($name) {
|
||||
case 'timestamp':
|
||||
return $this->dateTimeObject->getTimestamp();
|
||||
case 'iso':
|
||||
return $this->dateTimeObject->format('c');
|
||||
case 'timezone':
|
||||
return $this->dateTimeObject->format('e');
|
||||
case 'full':
|
||||
return $this->dateTimeObject;
|
||||
case 'accessTime':
|
||||
return $this->dateTimeObject->getTimestamp() - ($this->dateTimeObject->getTimestamp() % 60);
|
||||
}
|
||||
throw new AspectPropertyNotFoundException('Property "' . $name . '" not found in Aspect "' . __CLASS__ . '".', 1527778767);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the full date time object
|
||||
*/
|
||||
public function getDateTime(): \DateTimeImmutable
|
||||
{
|
||||
return $this->dateTimeObject;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Core\Context\Exception;
|
||||
|
||||
use TYPO3\CMS\Core\Exception;
|
||||
|
||||
/**
|
||||
* Used when an aspect is requested but not found.
|
||||
*/
|
||||
class AspectNotFoundException extends Exception {}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Core\Context\Exception;
|
||||
|
||||
use TYPO3\CMS\Core\Exception;
|
||||
|
||||
/**
|
||||
* Used when an aspect property is requested but not found.
|
||||
*/
|
||||
class AspectPropertyNotFoundException extends Exception {}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Core\Context;
|
||||
|
||||
use TYPO3\CMS\Core\Context\Exception\AspectPropertyNotFoundException;
|
||||
|
||||
/**
|
||||
* The aspect contains the processed file representation that is requested to process locally.
|
||||
*
|
||||
* Allowed properties:
|
||||
* - deferProcessing
|
||||
*/
|
||||
final readonly class FileProcessingAspect implements AspectInterface
|
||||
{
|
||||
public function __construct(
|
||||
private bool $deferProcessing = true,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Fetch the values
|
||||
*
|
||||
* @throws AspectPropertyNotFoundException
|
||||
*/
|
||||
public function get(string $name): bool
|
||||
{
|
||||
if ($name === 'deferProcessing') {
|
||||
return $this->deferProcessing;
|
||||
}
|
||||
throw new AspectPropertyNotFoundException('Property "' . $name . '" not found in Aspect "' . __CLASS__ . '".', 1599164743);
|
||||
}
|
||||
|
||||
public function isProcessingDeferred(): bool
|
||||
{
|
||||
return $this->deferProcessing;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Core\Context;
|
||||
|
||||
use TYPO3\CMS\Core\Context\Exception\AspectPropertyNotFoundException;
|
||||
|
||||
/**
|
||||
* The Aspect is usually available as "language" property, and
|
||||
* can be used to find out the "overlay"/data retrieval strategy.
|
||||
*
|
||||
*
|
||||
* "id" (languageId, int)
|
||||
* - the requested language of the current page (frontend)
|
||||
* - used in menus and links to generate "links in language with this ID"
|
||||
*
|
||||
* "contentId" (int)
|
||||
* - the language of records to be fetched
|
||||
* - if empty, "languageId" is used.
|
||||
*
|
||||
* "fallbackChain"
|
||||
* - when "fallback" go with
|
||||
* - depends on what "contentId" value should be set
|
||||
* - defined in config.sys_language_mode (strict/content_fallback:4,5,stop/ignore?)
|
||||
* - defines "contentId" based on "if the current page is available in this language"
|
||||
* - "strict"
|
||||
* - "fallback" if current page is not available, check the "fallbackChain"
|
||||
* - "fallbackAndIgnore"
|
||||
*
|
||||
* "overlayType"
|
||||
* - defines which way the records should be fetched from
|
||||
* - usually you fetch language 0 and -1, then take the "contentId" and "overlay" them
|
||||
* - here you have two choices
|
||||
* 1. "on" if there is no overlay, do not render the default language records ("hideNonTranslated")
|
||||
* 2. "mixed" - if there is no overlay, just keep the default language, possibility to have mixed languages - config.sys_language_overlay = 1
|
||||
* 3. "off" - do not do overlay, only fetch records available in the current "contentId" (see above), and do not care about overlays or fallbacks - fallbacks could be an option here, actually that is placed on top
|
||||
* 4. "includeFloating" - on + includeRecordsWithoutDefaultTranslation
|
||||
*/
|
||||
final readonly class LanguageAspect implements AspectInterface
|
||||
{
|
||||
public const string OVERLAYS_OFF = 'off'; // config.sys_language_overlay = 0
|
||||
public const string OVERLAYS_MIXED = 'mixed'; // config.sys_language_overlay = 1 (keep the ones that are only available in default language)
|
||||
public const string OVERLAYS_ON = 'on'; // "hideNonTranslated"
|
||||
public const string OVERLAYS_ON_WITH_FLOATING = 'includeFloating'; // "hideNonTranslated" + records that are only available in polish
|
||||
|
||||
/**
|
||||
* Create the default language
|
||||
*/
|
||||
public function __construct(
|
||||
private int $id = 0,
|
||||
private ?int $contentId = null,
|
||||
private string $overlayType = self::OVERLAYS_ON_WITH_FLOATING,
|
||||
private array $fallbackChain = [],
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Used language overlay
|
||||
*/
|
||||
public function getOverlayType(): string
|
||||
{
|
||||
return $this->overlayType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the language ID the current page was requested,
|
||||
* this is relevant when building menus or links to other pages.
|
||||
*/
|
||||
public function getId(): int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains the language UID of the content records that should be overlaid to would be fetched.
|
||||
* This is especially useful when a page requested with language=4 should fall back to showing
|
||||
* content of language=2 (see fallbackChain)
|
||||
*/
|
||||
public function getContentId(): int
|
||||
{
|
||||
return $this->contentId ?? $this->id;
|
||||
}
|
||||
|
||||
public function getFallbackChain(): array
|
||||
{
|
||||
return $this->fallbackChain;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether overlays should be done
|
||||
*/
|
||||
public function doOverlays(): bool
|
||||
{
|
||||
return $this->getContentId() > 0 && $this->overlayType !== self::OVERLAYS_OFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Here for compatibility reasons
|
||||
*/
|
||||
public function getLegacyLanguageMode(): string
|
||||
{
|
||||
if ($this->fallbackChain === ['off']) {
|
||||
return '';
|
||||
}
|
||||
if (empty($this->fallbackChain)) {
|
||||
return 'strict';
|
||||
}
|
||||
return 'content_fallback';
|
||||
}
|
||||
|
||||
/**
|
||||
* Here for compatibility reasons
|
||||
*/
|
||||
public function getLegacyOverlayType(): string
|
||||
{
|
||||
return match ($this->overlayType) {
|
||||
self::OVERLAYS_ON_WITH_FLOATING, self::OVERLAYS_ON => 'hideNonTranslated',
|
||||
self::OVERLAYS_MIXED => '1',
|
||||
default => '0',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a property.
|
||||
*
|
||||
* @throws AspectPropertyNotFoundException
|
||||
*/
|
||||
public function get(string $name): int|string|array
|
||||
{
|
||||
switch ($name) {
|
||||
case 'id':
|
||||
return $this->id;
|
||||
case 'contentId':
|
||||
return $this->getContentId();
|
||||
case 'fallbackChain':
|
||||
return $this->fallbackChain;
|
||||
case 'overlayType':
|
||||
return $this->overlayType;
|
||||
case 'legacyLanguageMode':
|
||||
return $this->getLegacyLanguageMode();
|
||||
case 'legacyOverlayType':
|
||||
return $this->getLegacyOverlayType();
|
||||
}
|
||||
throw new AspectPropertyNotFoundException('Property "' . $name . '" not found in Aspect "' . __CLASS__ . '".', 1530448504);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Core\Context;
|
||||
|
||||
use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
|
||||
|
||||
/**
|
||||
* A simple factory to create a language aspect.
|
||||
*/
|
||||
final readonly class LanguageAspectFactory
|
||||
{
|
||||
/**
|
||||
* Site Languages always run with overlays + floating records.
|
||||
*/
|
||||
public static function createFromSiteLanguage(SiteLanguage $language): LanguageAspect
|
||||
{
|
||||
$languageId = $language->getLanguageId();
|
||||
$fallbackType = $language->getFallbackType();
|
||||
$fallbackOrder = $language->getFallbackLanguageIds();
|
||||
$fallbackOrder[] = 'pageNotFound';
|
||||
switch ($fallbackType) {
|
||||
// Fall back to other language, if the page does not exist in the requested language
|
||||
// But always fetch only records of this specific (available) language
|
||||
case 'free':
|
||||
$overlayType = LanguageAspect::OVERLAYS_OFF;
|
||||
break;
|
||||
|
||||
// Fall back to other language, if the page does not exist in the requested language
|
||||
// Do overlays, and keep the ones that are not translated
|
||||
case 'fallback':
|
||||
$overlayType = LanguageAspect::OVERLAYS_MIXED;
|
||||
break;
|
||||
|
||||
// Same as "fallback" but remove the records that are not translated
|
||||
case 'strict':
|
||||
$overlayType = LanguageAspect::OVERLAYS_ON_WITH_FLOATING;
|
||||
break;
|
||||
|
||||
// Ignore, fallback to default language
|
||||
default:
|
||||
$fallbackOrder = [0];
|
||||
$overlayType = LanguageAspect::OVERLAYS_OFF;
|
||||
}
|
||||
return new LanguageAspect($languageId, $languageId, $overlayType, $fallbackOrder);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Core\Context;
|
||||
|
||||
use TYPO3\CMS\Core\Security\Nonce;
|
||||
use TYPO3\CMS\Core\Security\NoncePool;
|
||||
use TYPO3\CMS\Core\Security\RequestToken;
|
||||
use TYPO3\CMS\Core\Security\SigningSecretResolver;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class SecurityAspect implements AspectInterface
|
||||
{
|
||||
/**
|
||||
* `null` in case no request token was received
|
||||
* `false` in case a request token was received, which was invalid
|
||||
*/
|
||||
protected RequestToken|false|null $receivedRequestToken = null;
|
||||
|
||||
protected SigningSecretResolver $signingSecretResolver;
|
||||
|
||||
protected NoncePool $noncePool;
|
||||
|
||||
public static function provideIn(Context $context): self
|
||||
{
|
||||
if ($context->hasAspect('security')) {
|
||||
$securityAspect = $context->getAspect('security');
|
||||
}
|
||||
if (!isset($securityAspect) || !$securityAspect instanceof SecurityAspect) {
|
||||
$securityAspect = GeneralUtility::makeInstance(SecurityAspect::class);
|
||||
$context->setAspect('security', $securityAspect);
|
||||
}
|
||||
return $securityAspect;
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->noncePool = GeneralUtility::makeInstance(NoncePool::class);
|
||||
$this->signingSecretResolver = GeneralUtility::makeInstance(
|
||||
SigningSecretResolver::class,
|
||||
[
|
||||
'nonce' => $this->noncePool,
|
||||
// @todo enrich in separate step with `*FormProtection`
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function get(string $name): bool|Nonce|RequestToken|null
|
||||
{
|
||||
return match ($name) {
|
||||
'receivedRequestToken' => $this->receivedRequestToken,
|
||||
'signingSecretResolver' => $this->signingSecretResolver,
|
||||
'noncePool' => $this->noncePool,
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
public function getReceivedRequestToken(): RequestToken|false|null
|
||||
{
|
||||
return $this->receivedRequestToken;
|
||||
}
|
||||
|
||||
public function setReceivedRequestToken(RequestToken|false|null $receivedRequestToken): void
|
||||
{
|
||||
$this->receivedRequestToken = $receivedRequestToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves corresponding signing secret providers (such as `NoncePool`).
|
||||
* Example: `...->getSigningSecretResolver->findByType('nonce')` resolves `NoncePool`
|
||||
*/
|
||||
public function getSigningSecretResolver(): SigningSecretResolver
|
||||
{
|
||||
return $this->signingSecretResolver;
|
||||
}
|
||||
|
||||
public function getNoncePool(): NoncePool
|
||||
{
|
||||
return $this->noncePool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcut function to `NoncePool`, providing a `SigningSecret`
|
||||
* @todo this is a "comfort function", might be dropped
|
||||
*/
|
||||
public function provideNonce(): Nonce
|
||||
{
|
||||
return $this->noncePool->provideSigningSecret();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Core\Context;
|
||||
|
||||
use TYPO3\CMS\Core\Authentication\AbstractUserAuthentication;
|
||||
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
|
||||
use TYPO3\CMS\Core\Context\Exception\AspectPropertyNotFoundException;
|
||||
use TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication;
|
||||
|
||||
/**
|
||||
* The aspect contains information about a user.
|
||||
* Can be used for frontend and backend users.
|
||||
*
|
||||
* Allowed properties:
|
||||
* - id
|
||||
* - username
|
||||
* - isLoggedIn
|
||||
* - isAdmin
|
||||
* - groupIds (Array of Ids)
|
||||
* - groupNames
|
||||
*/
|
||||
final readonly class UserAspect implements AspectInterface
|
||||
{
|
||||
/**
|
||||
* @param AbstractUserAuthentication|null $user
|
||||
* @param array|null $alternativeGroups Alternative list of groups, usually useful for frontend logins with "magic" groups like "-1" and "-2"
|
||||
*/
|
||||
public function __construct(
|
||||
private ?AbstractUserAuthentication $user = null,
|
||||
private ?array $alternativeGroups = null
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Fetch common information about the user
|
||||
*
|
||||
* @throws AspectPropertyNotFoundException
|
||||
*/
|
||||
public function get(string $name): int|bool|string|array
|
||||
{
|
||||
switch ($name) {
|
||||
case 'id':
|
||||
return (int)($this->user?->user[$this->user->userid_column] ?? 0);
|
||||
case 'username':
|
||||
return (string)($this->user?->user[$this->user->username_column] ?? '');
|
||||
case 'isLoggedIn':
|
||||
return $this->isLoggedIn();
|
||||
case 'isAdmin':
|
||||
return $this->isAdmin();
|
||||
case 'groupIds':
|
||||
return $this->getGroupIds();
|
||||
case 'groupNames':
|
||||
return $this->getGroupNames();
|
||||
}
|
||||
throw new AspectPropertyNotFoundException('Property "' . $name . '" not found in Aspect "' . __CLASS__ . '".', 1529996567);
|
||||
}
|
||||
|
||||
/**
|
||||
* A user is logged in if the user has a UID, but does not care about groups.
|
||||
*
|
||||
* For frontend purposes, it is possible to e.g. simulate groups, but this would still be defined as "not logged in".
|
||||
*
|
||||
* For backend, only the check on the user ID is used.
|
||||
*/
|
||||
public function isLoggedIn(): bool
|
||||
{
|
||||
return ($this->user?->user[$this->user->userid_column] ?? 0) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if admin is set
|
||||
*/
|
||||
public function isAdmin(): bool
|
||||
{
|
||||
if ($this->user instanceof BackendUserAuthentication) {
|
||||
// Only backend users have the admin flag at all.
|
||||
return $this->user->isAdmin();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the groups the user is a member of
|
||||
*
|
||||
* For Frontend Users there are two special groups:
|
||||
* "-1" = hide at login
|
||||
* "-2" = show at any login
|
||||
*/
|
||||
public function getGroupIds(): array
|
||||
{
|
||||
// Alternative groups are set
|
||||
if (is_array($this->alternativeGroups)) {
|
||||
return $this->alternativeGroups;
|
||||
}
|
||||
if ($this->user instanceof BackendUserAuthentication) {
|
||||
return $this->user->userGroupsUID;
|
||||
}
|
||||
$groups = [];
|
||||
if ($this->user instanceof FrontendUserAuthentication) {
|
||||
if ($this->isLoggedIn()) {
|
||||
// If a user is logged in, always add "-2"
|
||||
$groups = [0, -2];
|
||||
if (!empty($this->user->userGroups)) {
|
||||
$groups = array_merge($groups, array_keys($this->user->userGroups));
|
||||
}
|
||||
} else {
|
||||
$groups = [0, -1];
|
||||
}
|
||||
}
|
||||
return $groups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of all groups, used in Fluid's IfHasRole ViewHelper
|
||||
*/
|
||||
public function getGroupNames(): array
|
||||
{
|
||||
$groupNames = [];
|
||||
if ($this->user instanceof AbstractUserAuthentication) {
|
||||
foreach ($this->user->userGroups as $userGroup) {
|
||||
$groupNames[] = $userGroup['title'];
|
||||
}
|
||||
}
|
||||
return $groupNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checking if a user is logged in or a group constellation different from "0,-1"
|
||||
*
|
||||
* @return bool TRUE if either a login user is found OR if the group list is set to something else than '0,-1' (could be done even without a user being logged in!)
|
||||
*/
|
||||
public function isUserOrGroupSet(): bool
|
||||
{
|
||||
if ($this->user instanceof FrontendUserAuthentication) {
|
||||
$groups = $this->getGroupIds();
|
||||
return $this->isLoggedIn() || implode(',', $groups) !== '0,-1';
|
||||
}
|
||||
return $this->isLoggedIn();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Core\Context;
|
||||
|
||||
use TYPO3\CMS\Core\Context\Exception\AspectPropertyNotFoundException;
|
||||
|
||||
/**
|
||||
* The aspect contains whether to show hidden pages, records (content) or even deleted records.
|
||||
*
|
||||
* Allowed properties:
|
||||
* - includeHiddenPages
|
||||
* - includeHiddenContent
|
||||
* - includeDeletedRecords
|
||||
* - includeScheduledRecords
|
||||
*/
|
||||
final readonly class VisibilityAspect implements AspectInterface
|
||||
{
|
||||
/**
|
||||
* @param bool $includeHiddenPages whether to include hidden=1 in pages tables
|
||||
* @param bool $includeHiddenContent whether to include hidden=1 in tables except for pages
|
||||
* @param bool $includeScheduledRecords whether to ignore access time in tables
|
||||
* @param bool $includeDeletedRecords whether to include deleted=1 records (only for use in recycler)
|
||||
*/
|
||||
public function __construct(
|
||||
private bool $includeHiddenPages = false,
|
||||
private bool $includeHiddenContent = false,
|
||||
private bool $includeDeletedRecords = false,
|
||||
private bool $includeScheduledRecords = false,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Fetch the values
|
||||
*
|
||||
* @throws AspectPropertyNotFoundException
|
||||
*/
|
||||
public function get(string $name): bool
|
||||
{
|
||||
switch ($name) {
|
||||
case 'includeHiddenPages':
|
||||
return $this->includeHiddenPages;
|
||||
case 'includeHiddenContent':
|
||||
return $this->includeHiddenContent;
|
||||
case 'includeDeletedRecords':
|
||||
return $this->includeDeletedRecords;
|
||||
case 'includeScheduledRecords':
|
||||
return $this->includeScheduledRecords;
|
||||
}
|
||||
throw new AspectPropertyNotFoundException('Property "' . $name . '" not found in Aspect "' . __CLASS__ . '".', 1527780439);
|
||||
}
|
||||
|
||||
public function includeHidden(): bool
|
||||
{
|
||||
return $this->includeHiddenContent || $this->includeHiddenPages;
|
||||
}
|
||||
|
||||
public function includeHiddenPages(): bool
|
||||
{
|
||||
return $this->includeHiddenPages;
|
||||
}
|
||||
|
||||
public function includeHiddenContent(): bool
|
||||
{
|
||||
return $this->includeHiddenContent;
|
||||
}
|
||||
|
||||
public function includeScheduledRecords(): bool
|
||||
{
|
||||
return $this->includeScheduledRecords;
|
||||
}
|
||||
|
||||
public function includeDeletedRecords(): bool
|
||||
{
|
||||
return $this->includeDeletedRecords;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Core\Context;
|
||||
|
||||
use TYPO3\CMS\Core\Context\Exception\AspectPropertyNotFoundException;
|
||||
|
||||
/**
|
||||
* The aspect contains information about the currently accessed workspace.
|
||||
*
|
||||
* Allowed properties:
|
||||
* - id
|
||||
* - isLive
|
||||
* - isOffline
|
||||
*/
|
||||
final readonly class WorkspaceAspect implements AspectInterface
|
||||
{
|
||||
public function __construct(
|
||||
private int $workspaceId = 0,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Fetch the workspace ID, or evaluated the state if it's 'online' or 'offline'
|
||||
*
|
||||
* @throws AspectPropertyNotFoundException
|
||||
*/
|
||||
public function get(string $name): int|bool
|
||||
{
|
||||
switch ($name) {
|
||||
case 'id':
|
||||
return $this->workspaceId;
|
||||
case 'isLive':
|
||||
return $this->isLive();
|
||||
case 'isOffline':
|
||||
return !$this->isLive();
|
||||
}
|
||||
throw new AspectPropertyNotFoundException('Property "' . $name . '" not found in Aspect "' . __CLASS__ . '".', 1527779447);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the workspace ID
|
||||
*/
|
||||
public function getId(): int
|
||||
{
|
||||
return $this->workspaceId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this is live workspace or in a custom offline workspace
|
||||
*/
|
||||
public function isLive(): bool
|
||||
{
|
||||
return $this->workspaceId === 0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user