Files
cms-backend/Classes/View/PageLayoutContext.php
T

329 lines
12 KiB
PHP

<?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\Backend\View;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Context\PageContext;
use TYPO3\CMS\Backend\Domain\Model\Language\PageLanguageInformation;
use TYPO3\CMS\Backend\Domain\Repository\Localization\LocalizationRepository;
use TYPO3\CMS\Backend\View\BackendLayout\BackendLayout;
use TYPO3\CMS\Backend\View\BackendLayout\ContentFetcher;
use TYPO3\CMS\Backend\View\Drawing\DrawingConfiguration;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Domain\Persistence\RecordIdentityMap;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Schema\Capability\TcaSchemaCapability;
use TYPO3\CMS\Core\Schema\TcaSchemaFactory;
use TYPO3\CMS\Core\Site\Entity\SiteInterface;
use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
use TYPO3\CMS\Core\Type\Bitmask\Permission;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Page Module specific rendering context.
*
* Extends generic PageContext with module-specific rendering configuration
* for the page module (web_layout).
*
* This context provides:
* - Generic page data (via delegation to PageContext)
* - Backend layout configuration
* - Drawing configuration
* - Content type labels
* - Content fetcher
*
* @internal
*/
class PageLayoutContext
{
protected ContentFetcher $contentFetcher;
protected ?array $localizedPageRecord = null;
/**
* @var SiteLanguage[]
*/
protected array $siteLanguages = [];
protected SiteLanguage $siteLanguage;
/**
* Array of content type labels. Key is CType, value is either a plain text
* label or an LLL:EXT:... reference to a specific label.
*/
protected array $contentTypeLabels = [];
/**
* Labels for columns, in format of TCA select options. Numerically indexed
* array of numerically indexed value arrays, with each sub-array containing
* at least two values and one optional third value:
*
* - label (hardcoded or LLL:EXT:... reference. MANDATORY)
* - value (colPos of column. MANDATORY)
* - icon (icon name or file reference. OPTIONAL)
*/
protected array $itemLabels = [];
protected RecordIdentityMap $recordIdentityMap;
public function __construct(
protected readonly PageContext $pageContext,
protected readonly BackendLayout $backendLayout,
protected readonly DrawingConfiguration $drawingConfiguration,
protected readonly ServerRequestInterface $request,
) {
$this->contentFetcher = GeneralUtility::makeInstance(ContentFetcher::class);
$this->siteLanguages = $this->pageContext->site->getAvailableLanguages($this->getBackendUser(), true, $this->pageContext->pageId);
$this->siteLanguage = $this->pageContext->site->getDefaultLanguage();
$this->recordIdentityMap = GeneralUtility::makeInstance(RecordIdentityMap::class);
}
public function cloneForLanguage(SiteLanguage $language): self
{
$copy = clone $this;
$copy->setSiteLanguage($language);
return $copy;
}
protected function setSiteLanguage(SiteLanguage $siteLanguage): void
{
$this->siteLanguage = $siteLanguage;
$languageId = $siteLanguage->getLanguageId();
if ($languageId > 0) {
$pageLocalizationRecord = GeneralUtility::makeInstance(LocalizationRepository::class)
->getPageTranslations($this->getPageId(), [$languageId], $this->getBackendUser()->workspace);
if ($pageLocalizationRecord !== []) {
// @todo: lets move to Record API soon
$pageLocalizationRecord = reset($pageLocalizationRecord);
$this->localizedPageRecord = $pageLocalizationRecord->toArray(true) ?: null;
}
}
}
public function getPageContext(): PageContext
{
return $this->pageContext;
}
public function getPageId(): int
{
return $this->pageContext->pageId;
}
public function getPageRecord(): array
{
return $this->pageContext->pageRecord;
}
public function getSite(): SiteInterface
{
return $this->pageContext->site;
}
public function getSelectedLanguageIds(): array
{
return $this->pageContext->selectedLanguageIds;
}
public function getPrimaryLanguageId(): int
{
return $this->pageContext->getPrimaryLanguageId();
}
public function getLanguageInformation(): PageLanguageInformation
{
return $this->pageContext->languageInformation;
}
public function getBackendLayout(): BackendLayout
{
return $this->backendLayout;
}
public function getDrawingConfiguration(): DrawingConfiguration
{
return $this->drawingConfiguration;
}
/**
* @return SiteLanguage[]
*/
public function getSiteLanguages(): iterable
{
return $this->siteLanguages;
}
/**
* @return SiteLanguage[]
*/
public function getLanguagesToShow(): iterable
{
$site = $this->pageContext->site;
$selectedLanguageIds = $this->drawingConfiguration->getSelectedLanguageIds();
// If multiple languages are selected, show default language + all selected languages
if (count($selectedLanguageIds) > 1 || (count($selectedLanguageIds) === 1 && $selectedLanguageIds[0] > 0)) {
$languagesToShow = [];
// Always include default language (0) first
$languagesToShow[] = $site->getDefaultLanguage();
// Add all selected languages, except default
foreach ($selectedLanguageIds as $languageId) {
try {
if ($languageId > 0) {
$languagesToShow[] = $site->getLanguageById($languageId);
}
} catch (\InvalidArgumentException $e) {
// Skip invalid language IDs
}
}
return $languagesToShow;
}
// Single language selected (default language only)
return [$site->getDefaultLanguage()];
}
public function hasMultiLanguages(): bool
{
return count($this->getLanguagesToShow()) > 1;
}
public function getSiteLanguage(?int $languageId = null): SiteLanguage
{
if ($languageId === null) {
return $this->siteLanguage;
}
if ($languageId === -1) {
return $this->siteLanguages[-1];
}
return $this->pageContext->site->getLanguageById($languageId);
}
public function isPageEditable(): bool
{
// TODO: refactor to page permissions container
if ($this->getBackendUser()->isAdmin()) {
return true;
}
$pageRecord = $this->pageContext->pageRecord;
return $this->getBackendUser()->doesUserHaveAccess($pageRecord, Permission::PAGE_EDIT)
&& (
!($schema = GeneralUtility::makeInstance(TcaSchemaFactory::class)->get('pages'))->hasCapability(TcaSchemaCapability::EditLock)
|| !($pageRecord[$schema->getCapability(TcaSchemaCapability::EditLock)->getFieldName()] ?? false)
);
}
public function getAllowNewContent(): bool
{
$allowInconsistentLanguageHandling = $this->drawingConfiguration->getAllowInconsistentLanguageHandling();
if (!$allowInconsistentLanguageHandling && $this->getLanguageModeIdentifier() === 'connected') {
return false;
}
return true;
}
public function getContentTypeLabels(): array
{
if (empty($this->contentTypeLabels)) {
$schemaFactory = GeneralUtility::makeInstance(TcaSchemaFactory::class);
$schema = $schemaFactory->get('tt_content');
if ($schema->supportsSubSchema()) {
if (($schemaTypeInformation = $schema->getSubSchemaTypeInformation())->isPointerToForeignFieldInForeignSchema()) {
$typeField = $schemaFactory->get($schemaTypeInformation->getForeignSchemaName())->getField($schemaTypeInformation->getForeignFieldName());
} else {
$typeField = $schema->getField($schemaTypeInformation->getFieldName());
}
foreach ($typeField->getConfiguration()['items'] ?? [] as $val) {
$this->contentTypeLabels[$val['value']] = $this->getLanguageService()->sL($val['label']);
}
}
}
return $this->contentTypeLabels;
}
public function getItemLabels(): array
{
if (empty($this->itemLabels)) {
foreach (GeneralUtility::makeInstance(TcaSchemaFactory::class)->get('tt_content')->getFields() as $field) {
$this->itemLabels[$field->getName()] = $this->getLanguageService()->sL($field->getLabel());
}
}
return $this->itemLabels;
}
public function getLanguageModeLabelClass(): string
{
$languageId = $this->siteLanguage->getLanguageId();
$contentRecordsPerColumn = $this->contentFetcher->getFlatContentRecords($this, $languageId);
$translationData = $this->contentFetcher->getTranslationData($this, $contentRecordsPerColumn, $languageId);
return $translationData['mode'] === 'mixed' ? 'danger' : 'info';
}
public function getLanguageMode(): string
{
return match ($this->getLanguageModeIdentifier()) {
'mixed' => $this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_layout.xlf:languageModeMixed'),
'connected' => $this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_layout.xlf:languageModeConnected'),
'free' => $this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_layout.xlf:languageModeFree'),
default => '',
};
}
public function getLanguageModeIdentifier(): string
{
$contentRecordsPerColumn = $this->contentFetcher->getContentRecordsPerColumn($this, null, $this->siteLanguage->getLanguageId());
$contentRecords = empty($contentRecordsPerColumn) ? [] : array_merge(...$contentRecordsPerColumn);
$translationData = $this->contentFetcher->getTranslationData($this, $contentRecords, $this->siteLanguage->getLanguageId());
return $translationData['mode'] ?? '';
}
public function getCurrentRequest(): ServerRequestInterface
{
return $this->request;
}
public function getLocalizedPageTitle(): string
{
return $this->localizedPageRecord['title'] ?? $this->pageContext->pageRecord['title'] ?? '';
}
public function getLocalizedPageRecord(): ?array
{
return $this->localizedPageRecord;
}
public function getRecordIdentityMap(): RecordIdentityMap
{
return $this->recordIdentityMap;
}
public function getReturnUrl(): string
{
return $this->getCurrentRequest()->getAttribute('normalizedParams')->getRequestUri();
}
protected function getLanguageService(): LanguageService
{
return $GLOBALS['LANG'];
}
public function getBackendUser(): BackendUserAuthentication
{
return $GLOBALS['BE_USER'];
}
}