├── Classes
├── Aspect
│ ├── FileMetadataOverlayAspect.php
│ └── PreviewAspect.php
├── Authentication
│ ├── FrontendBackendUserAuthentication.php
│ ├── FrontendUserAuthentication.php
│ └── ModifyResolvedFrontendGroupsEvent.php
├── Cache
│ ├── CacheInstruction.php
│ ├── CacheLifetimeCalculator.php
│ ├── MetaDataState.php
│ └── NonceValueSubstitution.php
├── Category
│ └── Collection
│ │ └── CategoryCollection.php
├── Content
│ ├── ContentSlideMode.php
│ └── RecordCollector.php
├── ContentObject
│ ├── AbstractContentObject.php
│ ├── CaseContentObject.php
│ ├── ContentContentObject.php
│ ├── ContentDataProcessor.php
│ ├── ContentObjectArrayContentObject.php
│ ├── ContentObjectArrayInternalContentObject.php
│ ├── ContentObjectFactory.php
│ ├── ContentObjectGetPublicUrlForFileHookInterface.php
│ ├── ContentObjectRenderer.php
│ ├── DataProcessorInterface.php
│ ├── Event
│ │ ├── AfterContentObjectRendererInitializedEvent.php
│ │ ├── AfterGetDataResolvedEvent.php
│ │ ├── AfterImageResourceResolvedEvent.php
│ │ ├── AfterStdWrapFunctionsExecutedEvent.php
│ │ ├── AfterStdWrapFunctionsInitializedEvent.php
│ │ ├── BeforeStdWrapContentStoredInCacheEvent.php
│ │ ├── BeforeStdWrapFunctionsExecutedEvent.php
│ │ ├── BeforeStdWrapFunctionsInitializedEvent.php
│ │ ├── EnhanceStdWrapEvent.php
│ │ ├── ModifyImageSourceCollectionEvent.php
│ │ └── ModifyRecordsAfterFetchingContentEvent.php
│ ├── Exception
│ │ ├── ContentRenderingException.php
│ │ ├── ExceptionHandlerInterface.php
│ │ └── ProductionExceptionHandler.php
│ ├── FileLinkHookInterface.php
│ ├── FilesContentObject.php
│ ├── FluidTemplateContentObject.php
│ ├── HierarchicalMenuContentObject.php
│ ├── ImageContentObject.php
│ ├── ImageResourceContentObject.php
│ ├── LoadRegisterContentObject.php
│ ├── Menu
│ │ ├── AbstractMenuContentObject.php
│ │ ├── CategoryMenuUtility.php
│ │ ├── Exception
│ │ │ └── NoSuchMenuTypeException.php
│ │ ├── MenuContentObjectFactory.php
│ │ └── TextMenuContentObject.php
│ ├── PageViewContentObject.php
│ ├── RecordsContentObject.php
│ ├── RestoreRegisterContentObject.php
│ ├── ScalableVectorGraphicsContentObject.php
│ ├── TextContentObject.php
│ ├── UserContentObject.php
│ └── UserInternalContentObject.php
├── Controller
│ ├── ErrorController.php
│ ├── ShowImageController.php
│ └── TypoScriptFrontendController.php
├── DataProcessing
│ ├── CommaSeparatedValueProcessor.php
│ ├── DataProcessorRegistry.php
│ ├── DatabaseQueryProcessor.php
│ ├── FilesProcessor.php
│ ├── FlexFormProcessor.php
│ ├── GalleryProcessor.php
│ ├── LanguageMenuProcessor.php
│ ├── MenuProcessor.php
│ ├── PageContentFetchingProcessor.php
│ ├── RecordTransformationProcessor.php
│ ├── SiteLanguageProcessor.php
│ ├── SiteProcessor.php
│ └── SplitProcessor.php
├── Event
│ ├── AfterCacheableContentIsGeneratedEvent.php
│ ├── AfterCachedPageIsPersistedEvent.php
│ ├── AfterContentHasBeenFetchedEvent.php
│ ├── AfterLinkIsGeneratedEvent.php
│ ├── AfterPageAndLanguageIsResolvedEvent.php
│ ├── AfterPageWithRootLineIsResolvedEvent.php
│ ├── AfterTypoScriptDeterminedEvent.php
│ ├── BeforePageCacheIdentifierIsHashedEvent.php
│ ├── BeforePageIsResolvedEvent.php
│ ├── FilterMenuItemsEvent.php
│ ├── ModifyCacheLifetimeForPageEvent.php
│ ├── ModifyCacheLifetimeForRowEvent.php
│ ├── ModifyHrefLangTagsEvent.php
│ ├── ModifyPageLinkConfigurationEvent.php
│ ├── ModifyTypoScriptConfigEvent.php
│ ├── ModifyTypoScriptConstantsEvent.php
│ └── ShouldUseCachedPageDataIfAvailableEvent.php
├── Exception.php
├── Html
│ └── HtmlWorker.php
├── Http
│ ├── Application.php
│ └── RequestHandler.php
├── Imaging
│ └── GifBuilder.php
├── Middleware
│ ├── BackendUserAuthenticator.php
│ ├── CacheTimeout.php
│ ├── ContentLengthResponseHeader.php
│ ├── ContentSecurityPolicyHeaders.php
│ ├── ContentSecurityPolicyReporter.php
│ ├── EidHandler.php
│ ├── FrontendUserAuthenticator.php
│ ├── MaintenanceMode.php
│ ├── OutputCompression.php
│ ├── PageArgumentValidator.php
│ ├── PageResolver.php
│ ├── PrepareTypoScriptFrontendRendering.php
│ ├── PreviewSimulator.php
│ ├── ShortcutAndMountPointRedirect.php
│ ├── SiteBaseRedirectResolver.php
│ ├── SiteResolver.php
│ ├── StaticRouteResolver.php
│ ├── TimeTrackerInitialization.php
│ └── TypoScriptFrontendInitialization.php
├── Page
│ ├── CacheHashCalculator.php
│ ├── CacheHashConfiguration.php
│ ├── PageAccessFailureReasons.php
│ ├── PageInformation.php
│ ├── PageInformationCreationFailedException.php
│ └── PageInformationFactory.php
├── Resource
│ ├── FileCollector.php
│ ├── FilePathSanitizer.php
│ └── PublicUrlPrefixer.php
├── ServiceProvider.php
├── Typolink
│ ├── AbstractTypolinkBuilder.php
│ ├── DatabaseRecordLinkBuilder.php
│ ├── EmailLinkBuilder.php
│ ├── ExternalUrlLinkBuilder.php
│ ├── FileOrFolderLinkBuilder.php
│ ├── LegacyLinkBuilder.php
│ ├── LinkFactory.php
│ ├── LinkResult.php
│ ├── LinkResultInterface.php
│ ├── LinkVarsCalculator.php
│ ├── PageLinkBuilder.php
│ ├── TelephoneLinkBuilder.php
│ └── UnableToLinkException.php
└── Utility
│ ├── CanonicalizationUtility.php
│ └── CompressionUtility.php
├── Configuration
├── ContentSecurityPolicies.php
├── RequestMiddlewares.php
├── Services.php
├── Services.yaml
├── TCA
│ ├── Overrides
│ │ ├── 100-sys_reaction.php
│ │ ├── 200-tt_content.php
│ │ ├── 220-tt_content-content_type-textpic.php
│ │ ├── 225-tt_content-content_type-image.php
│ │ ├── 230-tt_content-content_type-textmedia.php
│ │ ├── 235-tt_content-content_type-bullets.php
│ │ ├── 240-tt_content-content_type-table.php
│ │ ├── 245-tt_content-content_type-uploads.php
│ │ ├── 250-tt_content-content_type-menu_abstract.php
│ │ ├── 255-tt_content-content_type-menu_categorized_content.php
│ │ ├── 255-tt_content-content_type-menu_categorized_pages.php
│ │ ├── 260-tt_content-content_type-menu_pages.php
│ │ ├── 260-tt_content-content_type-menu_subpages.php
│ │ ├── 265-tt_content-content_type-menu_recently_updated.php
│ │ ├── 270-tt_content-content_type-menu_related_pages.php
│ │ ├── 275-tt_content-content_type-menu_section.php
│ │ ├── 275-tt_content-content_type-menu_section_pages.php
│ │ ├── 280-tt_content-content_type-menu_sitemap.php
│ │ ├── 280-tt_content-content_type-menu_sitemap_pages.php
│ │ ├── 285-tt_content-content_type-shortcut.php
│ │ ├── 290-tt_content-content_type-div.php
│ │ └── 295-tt_content-content_type-html.php
│ ├── backend_layout.php
│ ├── fe_groups.php
│ ├── fe_users.php
│ ├── sys_template.php
│ └── tt_content.php
├── page.tsconfig
└── user.tsconfig
├── LICENSE.txt
├── README.rst
├── Resources
├── Private
│ ├── Language
│ │ ├── locallang_tca.xlf
│ │ └── locallang_ttc.xlf
│ └── Templates
│ │ └── MainPage.html
└── Public
│ ├── Icons
│ ├── Extension.svg
│ └── FileIcons
│ │ ├── 3ds.gif
│ │ ├── CREDITS.txt
│ │ ├── ai.gif
│ │ ├── ani.gif
│ │ ├── au.gif
│ │ ├── avi.gif
│ │ ├── bmp.gif
│ │ ├── cdr.gif
│ │ ├── css.gif
│ │ ├── csv.gif
│ │ ├── default.gif
│ │ ├── doc.gif
│ │ ├── dtd.gif
│ │ ├── eps.gif
│ │ ├── exe.gif
│ │ ├── fh3.gif
│ │ ├── flash.gif
│ │ ├── folder.gif
│ │ ├── gif.gif
│ │ ├── htm.gif
│ │ ├── html.gif
│ │ ├── html1.gif
│ │ ├── html2.gif
│ │ ├── html3.gif
│ │ ├── ico.gif
│ │ ├── inc.gif
│ │ ├── java.gif
│ │ ├── jpg.gif
│ │ ├── js.gif
│ │ ├── max.gif
│ │ ├── mid.gif
│ │ ├── mov.gif
│ │ ├── mp3.gif
│ │ ├── mpeg.gif
│ │ ├── mpg.gif
│ │ ├── pcd.gif
│ │ ├── pcx.gif
│ │ ├── pdf.gif
│ │ ├── php3.gif
│ │ ├── png.gif
│ │ ├── ppt.gif
│ │ ├── ps.gif
│ │ ├── psd.gif
│ │ ├── rtf.gif
│ │ ├── sgml.gif
│ │ ├── swf.gif
│ │ ├── sxc.gif
│ │ ├── sxw.gif
│ │ ├── t3d.gif
│ │ ├── t3x.gif
│ │ ├── tga.gif
│ │ ├── tif.gif
│ │ ├── tmpl.gif
│ │ ├── ttf.gif
│ │ ├── txt.gif
│ │ ├── wav.gif
│ │ ├── wrl.gif
│ │ ├── xls.gif
│ │ ├── xml.gif
│ │ ├── xsl.gif
│ │ └── zip.gif
│ └── JavaScript
│ └── default_frontend.js
├── composer.json
├── ext_emconf.php
├── ext_localconf.php
└── ext_tables.sql
/Classes/Aspect/FileMetadataOverlayAspect.php:
--------------------------------------------------------------------------------
1 | isFrontend()
46 | || isset($_REQUEST['eID'])
47 | ) {
48 | return;
49 | }
50 | $overlaidMetaData = $event->getRecord();
51 | $pageRepository = GeneralUtility::makeInstance(PageRepository::class);
52 | $pageRepository->versionOL('sys_file_metadata', $overlaidMetaData);
53 | // getLanguageOverlay also calls versionOL() on the language overlaid record
54 | $overlaidMetaData = $pageRepository->getLanguageOverlay('sys_file_metadata', $overlaidMetaData);
55 | if ($overlaidMetaData !== null) {
56 | $event->setRecord($overlaidMetaData);
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/Classes/Aspect/PreviewAspect.php:
--------------------------------------------------------------------------------
1 | isPreview = $isPreview;
39 | }
40 |
41 | public function isPreview(): bool
42 | {
43 | return $this->isPreview;
44 | }
45 |
46 | /**
47 | * Get a property from aspect
48 | *
49 | * @return mixed
50 | * @throws AspectPropertyNotFoundException
51 | */
52 | public function get(string $name)
53 | {
54 | switch ($name) {
55 | case 'isPreview':
56 | return $this->isPreview;
57 | }
58 | throw new AspectPropertyNotFoundException('Property "' . $name . '" not found in Aspect "' . __CLASS__ . '".', 1563375558);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/Classes/Authentication/FrontendBackendUserAuthentication.php:
--------------------------------------------------------------------------------
1 | formfield_status is set to empty in order to
48 | * disable login-attempts to the backend account through this script
49 | *
50 | * @var string
51 | * @internal
52 | */
53 | protected $formfield_status = '';
54 |
55 | /**
56 | * Decides if the writelog() function is called at login and logout.
57 | *
58 | * @var bool
59 | */
60 | public $writeStdLog = false;
61 |
62 | /**
63 | * If the writelog() functions is called if a login-attempt has be tried without success.
64 | *
65 | * @var bool
66 | */
67 | public $writeAttemptLog = false;
68 |
69 | /**
70 | * Implementing the access checks that the TYPO3 CMS bootstrap script does before a user is ever logged in.
71 | * Used in the frontend.
72 | *
73 | * @return bool Returns TRUE if access is OK
74 | */
75 | public function backendCheckLogin(?ServerRequestInterface $request = null)
76 | {
77 | if (empty($this->user['uid'])) {
78 | return false;
79 | }
80 | // Check Hardcoded lock on BE
81 | if ($GLOBALS['TYPO3_CONF_VARS']['BE']['adminOnly'] < 0) {
82 | return false;
83 | }
84 | return $this->isUserAllowedToLogin();
85 | }
86 |
87 | /**
88 | * If a user is in a workspace, but previews the live workspace (GET keyword "LIVE") even if the user
89 | * has no editing permissions for this, it should still be visible, even though "be_users.workspace_perms" is set to "0".
90 | * If this ain't true, users without the live permission cannot see the live page, only the preview of the workspace of the user.
91 | */
92 | protected function hasEditAccessToLiveWorkspace(): bool
93 | {
94 | return true;
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/Classes/Authentication/ModifyResolvedFrontendGroupsEvent.php:
--------------------------------------------------------------------------------
1 | request;
37 | }
38 |
39 | public function getUser(): FrontendUserAuthentication
40 | {
41 | return $this->user;
42 | }
43 |
44 | public function getGroups(): array
45 | {
46 | return $this->groups;
47 | }
48 |
49 | public function setGroups(array $groups): void
50 | {
51 | $this->groups = $groups;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Classes/Cache/CacheInstruction.php:
--------------------------------------------------------------------------------
1 | allowCaching = false;
56 | $this->disabledCacheReasons[] = $reason;
57 | }
58 |
59 | public function isCachingAllowed(): bool
60 | {
61 | return $this->allowCaching;
62 | }
63 |
64 | /**
65 | * @internal Typically only consumed by extensions like EXT:adminpanel
66 | */
67 | public function getDisabledCacheReasons(): array
68 | {
69 | return $this->disabledCacheReasons;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/Classes/Cache/MetaDataState.php:
--------------------------------------------------------------------------------
1 | json_encode($this->policyRegistry->getMutationCollections()),
41 | ];
42 | }
43 |
44 | public function updateState(array $state): void
45 | {
46 | foreach ($state as $name => $value) {
47 | switch ($name) {
48 | case 'PolicyRegistry::$mutationCollections':
49 | $this->updatePolicyRegistryMutationCollections($value);
50 | break;
51 | }
52 | }
53 | }
54 |
55 | private function updatePolicyRegistryMutationCollections(mixed $value): void
56 | {
57 | if (!is_string($value) || $value === '') {
58 | return;
59 | }
60 | try {
61 | $array = json_decode($value, true, 512, \JSON_THROW_ON_ERROR);
62 | } catch (\JsonException) {
63 | return;
64 | }
65 | if (!is_array($array)) {
66 | return;
67 | }
68 | $this->policyRegistry->setMutationsCollections(
69 | ...array_map($this->modelService->buildMutationCollectionFromArray(...), $array)
70 | );
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/Classes/Cache/NonceValueSubstitution.php:
--------------------------------------------------------------------------------
1 | getAttribute('nonce');
31 | if ($currentNonce === null || empty($context['content']) || empty($context['nonce'])) {
32 | return null;
33 | }
34 | return str_replace($context['nonce'], $currentNonce->consume(), $context['content']);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Classes/Content/ContentSlideMode.php:
--------------------------------------------------------------------------------
1 | self::Slide,
31 | 'collect' => self::Collect,
32 | 'collectReverse' => self::CollectReverse,
33 | default => self::None,
34 | };
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Classes/Content/RecordCollector.php:
--------------------------------------------------------------------------------
1 | getRecords($table, $select);
64 | $recordsOnPid = array_map(
65 | fn(array $record): RecordInterface => $this->recordFactory->createResolvedRecordFromDatabaseRow($table, $record, null, $recordIdentityMap),
66 | $recordsOnPid
67 | );
68 |
69 | if ($slideCollectReverse) {
70 | $totalRecords = array_merge($totalRecords, $recordsOnPid);
71 | } else {
72 | $totalRecords = array_merge($recordsOnPid, $totalRecords);
73 | }
74 | if ($slide) {
75 | $select['pidInList'] = $contentObjectRenderer->getSlidePids($select['pidInList'] ?? '', $select['pidInList.'] ?? []);
76 | if (isset($select['pidInList.'])) {
77 | unset($select['pidInList.']);
78 | }
79 | $again = $select['pidInList'] !== '';
80 | }
81 | } while ($again && $slide && ($recordsOnPid === [] || $collect));
82 |
83 | foreach ($totalRecords as $record) {
84 | $contentObjectRenderer->lastChanged($record);
85 | }
86 | return $totalRecords;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/Classes/ContentObject/AbstractContentObject.php:
--------------------------------------------------------------------------------
1 | cObj ?? $this->getTypoScriptFrontendController()->cObj;
54 | }
55 |
56 | public function setRequest(ServerRequestInterface $request): void
57 | {
58 | $this->request = $request;
59 | }
60 |
61 | public function setContentObjectRenderer(ContentObjectRenderer $cObj): void
62 | {
63 | $this->cObj = $cObj;
64 | // Provide the ContentObjectRenderer to the request as well, for code
65 | // that only passes the request to more underlying layers, like Extbase does.
66 | // Also makes sure the request in a Fluid RenderingContext also has the current
67 | // content object available.
68 | $this->request = $this->request->withAttribute('currentContentObject', $cObj);
69 | }
70 |
71 | protected function hasTypoScriptFrontendController(): bool
72 | {
73 | return $this->cObj?->getTypoScriptFrontendController() instanceof TypoScriptFrontendController;
74 | }
75 |
76 | /**
77 | * @throws ContentRenderingException
78 | */
79 | protected function getTypoScriptFrontendController(): TypoScriptFrontendController
80 | {
81 | if (!$this->hasTypoScriptFrontendController()) {
82 | throw new ContentRenderingException('TypoScriptFrontendController is not available.', 1655723512);
83 | }
84 |
85 | return $this->cObj->getTypoScriptFrontendController();
86 | }
87 |
88 | protected function getPageRepository(): PageRepository
89 | {
90 | return GeneralUtility::makeInstance(PageRepository::class);
91 | }
92 |
93 | protected function getPageRenderer(): PageRenderer
94 | {
95 | if ($this->pageRenderer === null) {
96 | $this->pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
97 | }
98 |
99 | return $this->pageRenderer;
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/Classes/ContentObject/CaseContentObject.php:
--------------------------------------------------------------------------------
1 | cObj->checkIf($conf['if.'])) {
32 | return '';
33 | }
34 |
35 | $setCurrent = $this->cObj->stdWrapValue('setCurrent', $conf);
36 | if ($setCurrent) {
37 | $this->cObj->data[$this->cObj->currentValKey] = $setCurrent;
38 | }
39 | $key = $this->cObj->stdWrapValue('key', $conf, null);
40 | $key = isset($conf[$key]) && (string)$conf[$key] !== '' ? $key : 'default';
41 | // If no "default" property is available, then an empty string is returned
42 | if ($key === 'default' && !isset($conf['default'])) {
43 | $theValue = '';
44 | } else {
45 | $theValue = $this->cObj->cObjGetSingle($conf[$key], $conf[$key . '.'] ?? [], $key);
46 | }
47 | if (isset($conf['stdWrap.'])) {
48 | $theValue = $this->cObj->stdWrap($theValue, $conf['stdWrap.']);
49 | }
50 | return $theValue;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Classes/ContentObject/ContentObjectArrayContentObject.php:
--------------------------------------------------------------------------------
1 | getTimeTracker()->setTSlogMessage('No elements in this content object array (COBJ_ARRAY, COA).', LogLevel::WARNING);
37 | return '';
38 | }
39 | if (!empty($conf['if.']) && !$this->cObj->checkIf($conf['if.'])) {
40 | return '';
41 | }
42 |
43 | $content = $this->cObj->cObjGet($conf);
44 | $wrap = $this->cObj->stdWrapValue('wrap', $conf);
45 | if ($wrap) {
46 | $content = $this->cObj->wrap($content, $wrap);
47 | }
48 | if (isset($conf['stdWrap.'])) {
49 | $content = $this->cObj->stdWrap($content, $conf['stdWrap.']);
50 | }
51 | return $content;
52 | }
53 |
54 | /**
55 | * @return TimeTracker
56 | */
57 | protected function getTimeTracker()
58 | {
59 | return GeneralUtility::makeInstance(TimeTracker::class);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/Classes/ContentObject/ContentObjectArrayInternalContentObject.php:
--------------------------------------------------------------------------------
1 | getTimeTracker()->setTSlogMessage('No elements in this content object array (COA_INT).', LogLevel::WARNING);
37 | return '';
38 | }
39 |
40 | $frontendController = $this->getTypoScriptFrontendController();
41 | $substKey = 'INT_SCRIPT.' . $frontendController->uniqueHash();
42 | $content = '';
43 | $frontendController->config['INTincScript'][$substKey] = [
44 | 'conf' => $conf,
45 | 'cObj' => serialize($this->cObj),
46 | 'type' => 'COA',
47 | ];
48 | return $content;
49 | }
50 |
51 | /**
52 | * @return TimeTracker
53 | */
54 | protected function getTimeTracker()
55 | {
56 | return GeneralUtility::makeInstance(TimeTracker::class);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Classes/ContentObject/ContentObjectFactory.php:
--------------------------------------------------------------------------------
1 | contentObjectLocator->has($name)) {
35 | return null;
36 | }
37 |
38 | $contentObject = $this->contentObjectLocator->get($name);
39 | if (!($contentObject instanceof AbstractContentObject)) {
40 | throw new ContentRenderingException(sprintf('Registered content object class name "%s" must be an instance of AbstractContentObject, but is not!', get_class($contentObject)), 1422564295);
41 | }
42 |
43 | $contentObject->setRequest($request);
44 | $contentObject->setContentObjectRenderer($contentObjectRenderer);
45 |
46 | return $contentObject;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Classes/ContentObject/ContentObjectGetPublicUrlForFileHookInterface.php:
--------------------------------------------------------------------------------
1 | contentObjectRenderer;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Classes/ContentObject/Event/AfterGetDataResolvedEvent.php:
--------------------------------------------------------------------------------
1 | getData() result
24 | */
25 | final class AfterGetDataResolvedEvent
26 | {
27 | public function __construct(
28 | private readonly string $parameterString,
29 | private readonly array $alternativeFieldArray,
30 | private mixed $result,
31 | private readonly ContentObjectRenderer $contentObjectRenderer
32 | ) {}
33 |
34 | public function getResult(): mixed
35 | {
36 | return $this->result;
37 | }
38 |
39 | public function setResult(mixed $result): void
40 | {
41 | $this->result = $result;
42 | }
43 |
44 | public function getParameterString(): string
45 | {
46 | return $this->parameterString;
47 | }
48 |
49 | public function getAlternativeFieldArray(): array
50 | {
51 | return $this->alternativeFieldArray;
52 | }
53 |
54 | public function getContentObjectRenderer(): ContentObjectRenderer
55 | {
56 | return $this->contentObjectRenderer;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Classes/ContentObject/Event/AfterImageResourceResolvedEvent.php:
--------------------------------------------------------------------------------
1 | getImgResource() result
26 | */
27 | final class AfterImageResourceResolvedEvent
28 | {
29 | public function __construct(
30 | private readonly string|File|FileReference $file,
31 | private readonly array $fileArray,
32 | private ?ImageResource $imageResource
33 | ) {}
34 |
35 | public function getFile(): string|File|FileReference
36 | {
37 | return $this->file;
38 | }
39 |
40 | public function getFileArray(): array
41 | {
42 | return $this->fileArray;
43 | }
44 |
45 | public function getImageResource(): ?ImageResource
46 | {
47 | return $this->imageResource;
48 | }
49 |
50 | public function setImageResource(?ImageResource $imageResource): void
51 | {
52 | $this->imageResource = $imageResource;
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Classes/ContentObject/Event/AfterStdWrapFunctionsExecutedEvent.php:
--------------------------------------------------------------------------------
1 | content;
43 | }
44 |
45 | public function setContent(string $content): void
46 | {
47 | $this->content = $content;
48 | }
49 |
50 | public function getTags(): array
51 | {
52 | return $this->tags;
53 | }
54 |
55 | public function setTags(array $tags): void
56 | {
57 | $this->tags = $tags;
58 | }
59 |
60 | public function getKey(): string
61 | {
62 | return $this->key;
63 | }
64 |
65 | public function setKey(string $key): void
66 | {
67 | $this->key = $key;
68 | }
69 |
70 | public function getLifetime(): ?int
71 | {
72 | return $this->lifetime;
73 | }
74 |
75 | public function setLifetime(?int $lifetime): void
76 | {
77 | $this->lifetime = $lifetime;
78 | }
79 |
80 | public function getConfiguration(): array
81 | {
82 | return $this->configuration;
83 | }
84 |
85 | public function getContentObjectRenderer(): ContentObjectRenderer
86 | {
87 | return $this->contentObjectRenderer;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/Classes/ContentObject/Event/BeforeStdWrapFunctionsExecutedEvent.php:
--------------------------------------------------------------------------------
1 | content;
46 | }
47 |
48 | public function setContent(string $content): void
49 | {
50 | $this->content = $content;
51 | }
52 |
53 | public function getConfiguration(): array
54 | {
55 | return $this->configuration;
56 | }
57 |
58 | public function getContentObjectRenderer(): ContentObjectRenderer
59 | {
60 | return $this->contentObjectRenderer;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Classes/ContentObject/Event/ModifyImageSourceCollectionEvent.php:
--------------------------------------------------------------------------------
1 | sourceCollection = $sourceCollection;
38 | }
39 |
40 | public function getSourceCollection(): string
41 | {
42 | return $this->sourceCollection;
43 | }
44 |
45 | public function getFullSourceCollection(): string
46 | {
47 | return $this->fullSourceCollection;
48 | }
49 |
50 | public function getSourceConfiguration(): array
51 | {
52 | return $this->sourceConfiguration;
53 | }
54 |
55 | public function getSourceRenderConfiguration(): array
56 | {
57 | return $this->sourceRenderConfiguration;
58 | }
59 |
60 | public function getContentObjectRenderer(): ContentObjectRenderer
61 | {
62 | return $this->contentObjectRenderer;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Classes/ContentObject/Event/ModifyRecordsAfterFetchingContentEvent.php:
--------------------------------------------------------------------------------
1 | records;
44 | }
45 |
46 | public function setRecords(array $records): void
47 | {
48 | $this->records = $records;
49 | }
50 |
51 | public function getFinalContent(): string
52 | {
53 | return $this->finalContent;
54 | }
55 |
56 | public function setFinalContent(string $finalContent): void
57 | {
58 | $this->finalContent = $finalContent;
59 | }
60 |
61 | public function getSlide(): int
62 | {
63 | return $this->slide;
64 | }
65 |
66 | public function setSlide(int $slide): void
67 | {
68 | $this->slide = $slide;
69 | }
70 |
71 | public function getSlideCollect(): int
72 | {
73 | return $this->slideCollect;
74 | }
75 |
76 | public function setSlideCollect(int $slideCollect): void
77 | {
78 | $this->slideCollect = $slideCollect;
79 | }
80 |
81 | public function getSlideCollectReverse(): bool
82 | {
83 | return $this->slideCollectReverse;
84 | }
85 |
86 | public function setSlideCollectReverse(bool $slideCollectReverse): void
87 | {
88 | $this->slideCollectReverse = $slideCollectReverse;
89 | }
90 |
91 | public function getSlideCollectFuzzy(): bool
92 | {
93 | return $this->slideCollectFuzzy;
94 | }
95 |
96 | public function setSlideCollectFuzzy(bool $slideCollectFuzzy): void
97 | {
98 | $this->slideCollectFuzzy = $slideCollectFuzzy;
99 | }
100 |
101 | public function getConfiguration(): array
102 | {
103 | return $this->configuration;
104 | }
105 |
106 | public function setConfiguration(array $configuration): void
107 | {
108 | $this->configuration = $configuration;
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/Classes/ContentObject/Exception/ContentRenderingException.php:
--------------------------------------------------------------------------------
1 | configuration = $configuration;
48 | }
49 |
50 | /**
51 | * Handles exceptions thrown during rendering of content objects
52 | * The handler can decide whether to re-throw the exception or
53 | * return a nice error message for production context.
54 | *
55 | * @param AbstractContentObject|null $contentObject
56 | * @param array $contentObjectConfiguration
57 | * @throws \Exception
58 | */
59 | public function handle(\Exception $exception, ?AbstractContentObject $contentObject = null, $contentObjectConfiguration = []): string
60 | {
61 | // ImmediateResponseException (and the derived PropagateResponseException) should work similar to
62 | // exit / die and must therefore not be handled by this ExceptionHandler.
63 | if ($exception instanceof ImmediateResponseException) {
64 | throw $exception;
65 | }
66 |
67 | if (!empty($this->configuration['ignoreCodes.'])
68 | && in_array($exception->getCode(), array_map('intval', $this->configuration['ignoreCodes.']), true)
69 | ) {
70 | throw $exception;
71 | }
72 |
73 | $errorMessage = $this->configuration['errorMessage'] ?? 'Oops, an error occurred! Request: {requestId}';
74 |
75 | // $code and it's placeholder %s for b/w compatibility
76 | $code = $this->context->getAspect('date')->getDateTime()->format('YmdHis') . $this->random->generateRandomHexString(8);
77 | $errorMessage = str_replace('%s', '{code}', $errorMessage);
78 |
79 | // Log exception except HMAC validation exceptions caused by potentially forged requests
80 | if (!in_array($exception->getCode(), AbstractExceptionHandler::IGNORED_HMAC_EXCEPTION_CODES, true)) {
81 | $this->logger->alert($errorMessage, ['exception' => $exception, 'code' => $code, 'requestId' => $this->requestId]);
82 | }
83 |
84 | // Return interpolated error message
85 | return str_replace(['{code}', '{requestId}'], [$code, $this->requestId], $errorMessage);
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/Classes/ContentObject/FileLinkHookInterface.php:
--------------------------------------------------------------------------------
1 | cObj->checkIf($conf['if.'])) {
36 | return '';
37 | }
38 |
39 | $theValue = '';
40 | $menuType = $conf[1] ?? '';
41 | try {
42 | $frontendController = $this->getTypoScriptFrontendController();
43 | $menuObjectFactory = GeneralUtility::makeInstance(MenuContentObjectFactory::class);
44 | $menu = $menuObjectFactory->getMenuObjectByType($menuType);
45 | if (isset($frontendController->register['count_HMENU'])) {
46 | $frontendController->register['count_HMENU']++;
47 | } else {
48 | $frontendController->register['count_HMENU'] = 1;
49 | }
50 | $frontendController->register['count_HMENU_MENUOBJ'] = 0;
51 | $frontendController->register['count_MENUOBJ'] = 0;
52 | $menu->parent_cObj = $this->getContentObjectRenderer();
53 | $menu->start(null, $this->getPageRepository(), '', $conf, 1, '', $this->request);
54 | $menu->makeMenu();
55 | $theValue .= $menu->writeMenu();
56 | } catch (NoSuchMenuTypeException $e) {
57 | }
58 | $wrap = $this->cObj->stdWrapValue('wrap', $conf);
59 | if ($wrap) {
60 | $theValue = $this->cObj->wrap($theValue, $wrap);
61 | }
62 | if (isset($conf['stdWrap.'])) {
63 | $theValue = $this->cObj->stdWrap($theValue, $conf['stdWrap.']);
64 | }
65 | return $theValue;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Classes/ContentObject/ImageResourceContentObject.php:
--------------------------------------------------------------------------------
1 | cObj->getImgResource($conf['file'] ?? '', $conf['file.'] ?? []);
32 | if ($imageResource === null) {
33 | return '';
34 | }
35 | return isset($conf['stdWrap.'])
36 | ? $this->cObj->stdWrap($imageResource->getPublicUrl(), $conf['stdWrap.'])
37 | : $imageResource->getPublicUrl();
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Classes/ContentObject/LoadRegisterContentObject.php:
--------------------------------------------------------------------------------
1 | getTypoScriptFrontendController();
33 | $frontendController->registerStack[] = $frontendController->register;
34 | if (is_array($conf)) {
35 | $isExecuted = [];
36 | foreach ($conf as $theKey => $theValue) {
37 | $register = rtrim($theKey, '.');
38 | if (!isset($isExecuted[$register]) || !$isExecuted[$register]) {
39 | $registerProperties = $register . '.';
40 | if (isset($conf[$register]) && isset($conf[$registerProperties])) {
41 | $theValue = $this->cObj->stdWrap($conf[$register], $conf[$registerProperties]);
42 | } elseif (isset($conf[$registerProperties])) {
43 | $theValue = $this->cObj->stdWrap('', $conf[$registerProperties]);
44 | }
45 | $frontendController->register[$register] = $theValue;
46 | $isExecuted[$register] = true;
47 | }
48 | }
49 | }
50 | return '';
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Classes/ContentObject/Menu/Exception/NoSuchMenuTypeException.php:
--------------------------------------------------------------------------------
1 | TextMenuContentObject::class,
37 | ];
38 |
39 | /**
40 | * Gets a typo script string like 'TMENU' and returns an object of this type
41 | *
42 | * @throws Exception\NoSuchMenuTypeException
43 | */
44 | public function getMenuObjectByType(string $type = ''): AbstractMenuContentObject
45 | {
46 | $upperCasedClassName = strtoupper($type);
47 | if (array_key_exists($upperCasedClassName, $this->menuTypeToClassMapping)) {
48 | /** @var AbstractMenuContentObject $object */
49 | $object = GeneralUtility::makeInstance($this->menuTypeToClassMapping[$upperCasedClassName]);
50 | return $object;
51 | }
52 | throw new NoSuchMenuTypeException(
53 | 'Menu type ' . (string)$type . ' has no implementing class.',
54 | 1363278130
55 | );
56 | }
57 |
58 | /**
59 | * Register new menu type or override existing type
60 | *
61 | * @param string $type Menu type to be used in TypoScript
62 | * @param string $className Class rendering the menu
63 | */
64 | public function registerMenuType(string $type, string $className)
65 | {
66 | $this->menuTypeToClassMapping[strtoupper($type)] = $className;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/Classes/ContentObject/RestoreRegisterContentObject.php:
--------------------------------------------------------------------------------
1 | getTypoScriptFrontendController();
33 | $frontendController->register = array_pop($frontendController->registerStack) ?? [];
34 | return '';
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Classes/ContentObject/TextContentObject.php:
--------------------------------------------------------------------------------
1 | cObj->stdWrap($content, $conf['value.']);
41 | unset($conf['value.']);
42 | }
43 | if (!empty($conf)) {
44 | $content = $this->cObj->stdWrap($content, $conf);
45 | }
46 | return $content;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Classes/ContentObject/UserContentObject.php:
--------------------------------------------------------------------------------
1 | getTimeTracker()->setTSlogMessage('USER without configuration.', LogLevel::WARNING);
37 | return '';
38 | }
39 | $content = '';
40 | if ($this->cObj->getUserObjectType() === false) {
41 | // Come here only if we are not called from $TSFE->processNonCacheableContentPartsAndSubstituteContentMarkers()!
42 | $this->cObj->setUserObjectType(ContentObjectRenderer::OBJECTTYPE_USER);
43 | }
44 | $tempContent = $this->cObj->callUserFunction($conf['userFunc'] ?? '', $conf, '');
45 | if ($this->cObj->doConvertToUserIntObject) {
46 | $this->cObj->doConvertToUserIntObject = false;
47 | $content = $this->cObj->cObjGetSingle('USER_INT', $conf);
48 | } else {
49 | $content .= $tempContent;
50 | // Only executed when the element is not converted to USER_INT
51 | if (isset($conf['stdWrap.'])) {
52 | $content = $this->cObj->stdWrap($content, $conf['stdWrap.']);
53 | }
54 | }
55 | $this->cObj->setUserObjectType(false);
56 | return $content;
57 | }
58 |
59 | /**
60 | * @return TimeTracker
61 | */
62 | protected function getTimeTracker()
63 | {
64 | return GeneralUtility::makeInstance(TimeTracker::class);
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/Classes/ContentObject/UserInternalContentObject.php:
--------------------------------------------------------------------------------
1 | cObj->setUserObjectType(ContentObjectRenderer::OBJECTTYPE_USER_INT);
32 | $tsfe = $this->getTypoScriptFrontendController();
33 | $substKey = 'INT_SCRIPT.' . $tsfe->uniqueHash();
34 | $content = '';
35 | $tsfe->config['INTincScript'][$substKey] = [
36 | 'conf' => $conf,
37 | 'cObj' => serialize($this->cObj),
38 | 'type' => 'FUNC',
39 | ];
40 | $this->cObj->setUserObjectType(false);
41 | return $content;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Classes/DataProcessing/DataProcessorRegistry.php:
--------------------------------------------------------------------------------
1 | dataProcessorLocator->has($identifer)) {
34 | return null;
35 | }
36 |
37 | $dataProcessor = $this->dataProcessorLocator->get($identifer);
38 | if (!($dataProcessor instanceof DataProcessorInterface)) {
39 | throw new \UnexpectedValueException(
40 | 'Processor with alias / identifier "' . $identifer . '" ' .
41 | 'must implement interface "' . DataProcessorInterface::class . '"',
42 | 1666131903
43 | );
44 | }
45 |
46 | return $dataProcessor;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Classes/DataProcessing/SiteLanguageProcessor.php:
--------------------------------------------------------------------------------
1 | stdWrapValue('as', $processorConfiguration, 'siteLanguage');
47 | $processedData[$targetVariableName] = $cObj->getRequest()->getAttribute('language')?->toArray();
48 | return $processedData;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Classes/DataProcessing/SiteProcessor.php:
--------------------------------------------------------------------------------
1 | stdWrapValue('as', $processorConfiguration, 'site');
47 | $processedData[$targetVariableName] = $cObj->getRequest()->getAttribute('site');
48 | return $processedData;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Classes/Event/AfterCacheableContentIsGeneratedEvent.php:
--------------------------------------------------------------------------------
1 | isCachingEnabled() as the same as $TSFE->no_cache.
26 | * Depending on disable or enabling caching, the cache is then not stored in the pageCache.
27 | */
28 | final class AfterCacheableContentIsGeneratedEvent
29 | {
30 | public function __construct(
31 | private readonly ServerRequestInterface $request,
32 | private readonly TypoScriptFrontendController $controller,
33 | private readonly string $cacheIdentifier,
34 | private bool $usePageCache
35 | ) {}
36 |
37 | public function getRequest(): ServerRequestInterface
38 | {
39 | return $this->request;
40 | }
41 |
42 | public function getController(): TypoScriptFrontendController
43 | {
44 | return $this->controller;
45 | }
46 |
47 | public function isCachingEnabled(): bool
48 | {
49 | return $this->usePageCache;
50 | }
51 |
52 | public function disableCaching(): void
53 | {
54 | $this->usePageCache = false;
55 | }
56 |
57 | public function enableCaching(): void
58 | {
59 | $this->usePageCache = true;
60 | }
61 |
62 | public function getCacheIdentifier(): string
63 | {
64 | return $this->cacheIdentifier;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/Classes/Event/AfterCachedPageIsPersistedEvent.php:
--------------------------------------------------------------------------------
1 | request;
44 | }
45 |
46 | public function getController(): TypoScriptFrontendController
47 | {
48 | return $this->controller;
49 | }
50 |
51 | public function getCacheIdentifier(): string
52 | {
53 | return $this->cacheIdentifier;
54 | }
55 |
56 | public function getCacheData(): array
57 | {
58 | return $this->cacheData;
59 | }
60 |
61 | /**
62 | * The amount of seconds until the cache entry is invalid.
63 | */
64 | public function getCacheLifetime(): int
65 | {
66 | return $this->cacheLifetime;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/Classes/Event/AfterContentHasBeenFetchedEvent.php:
--------------------------------------------------------------------------------
1 | linkResult = $linkResult;
43 | }
44 |
45 | public function getLinkResult(): LinkResultInterface
46 | {
47 | return $this->linkResult;
48 | }
49 |
50 | public function getContentObjectRenderer(): ContentObjectRenderer
51 | {
52 | return $this->contentObjectRenderer;
53 | }
54 |
55 | /**
56 | * Returns the original instructions / $linkConfiguration that were used to build the link
57 | */
58 | public function getLinkInstructions(): array
59 | {
60 | return $this->linkInstructions;
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Classes/Event/AfterPageAndLanguageIsResolvedEvent.php:
--------------------------------------------------------------------------------
1 | request;
43 | }
44 |
45 | public function getPageInformation(): PageInformation
46 | {
47 | return $this->pageInformation;
48 | }
49 |
50 | public function setPageInformation(PageInformation $pageInformation): void
51 | {
52 | $this->pageInformation = $pageInformation;
53 | }
54 |
55 | public function getResponse(): ?ResponseInterface
56 | {
57 | return $this->response;
58 | }
59 |
60 | public function setResponse(ResponseInterface $response): void
61 | {
62 | $this->response = $response;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Classes/Event/AfterPageWithRootLineIsResolvedEvent.php:
--------------------------------------------------------------------------------
1 | request;
42 | }
43 |
44 | public function setResponse(ResponseInterface $response): void
45 | {
46 | $this->response = $response;
47 | }
48 |
49 | public function getResponse(): ?ResponseInterface
50 | {
51 | return $this->response;
52 | }
53 |
54 | public function getPageInformation(): PageInformation
55 | {
56 | return $this->pageInformation;
57 | }
58 |
59 | public function setPageInformation(PageInformation $pageInformation): void
60 | {
61 | $this->pageInformation = $pageInformation;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Classes/Event/AfterTypoScriptDeterminedEvent.php:
--------------------------------------------------------------------------------
1 | frontendTypoScript;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Classes/Event/BeforePageCacheIdentifierIsHashedEvent.php:
--------------------------------------------------------------------------------
1 | request;
46 | }
47 |
48 | public function getPageCacheIdentifierParameters(): array
49 | {
50 | return $this->pageCacheIdentifierParameters;
51 | }
52 |
53 | public function setPageCacheIdentifierParameters(array $pageCacheIdentifierParameters): void
54 | {
55 | $this->pageCacheIdentifierParameters = $pageCacheIdentifierParameters;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Classes/Event/BeforePageIsResolvedEvent.php:
--------------------------------------------------------------------------------
1 | id) or modify the context
28 | * for resolving a page.
29 | */
30 | final class BeforePageIsResolvedEvent
31 | {
32 | public function __construct(
33 | private readonly ServerRequestInterface $request,
34 | private PageInformation $pageInformation,
35 | ) {}
36 |
37 | public function getRequest(): ServerRequestInterface
38 | {
39 | return $this->request;
40 | }
41 |
42 | public function getPageInformation(): PageInformation
43 | {
44 | return $this->pageInformation;
45 | }
46 |
47 | public function setPageInformation(PageInformation $pageInformation): void
48 | {
49 | $this->pageInformation = $pageInformation;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/Classes/Event/FilterMenuItemsEvent.php:
--------------------------------------------------------------------------------
1 | allMenuItems;
43 | }
44 |
45 | public function getFilteredMenuItems(): array
46 | {
47 | return $this->filteredMenuItems;
48 | }
49 |
50 | public function setFilteredMenuItems(array $filteredMenuItems): void
51 | {
52 | $this->filteredMenuItems = $filteredMenuItems;
53 | }
54 |
55 | public function getMenuConfiguration(): array
56 | {
57 | return $this->menuConfiguration;
58 | }
59 |
60 | public function getItemConfiguration(): array
61 | {
62 | return $this->itemConfiguration;
63 | }
64 |
65 | public function getBannedMenuItems(): array
66 | {
67 | return $this->bannedMenuItems;
68 | }
69 |
70 | public function getExcludedDoktypes(): array
71 | {
72 | return $this->excludedDoktypes;
73 | }
74 |
75 | public function getSite(): Site
76 | {
77 | return $this->site;
78 | }
79 |
80 | public function getContext(): Context
81 | {
82 | return $this->context;
83 | }
84 |
85 | public function getCurrentPage(): array
86 | {
87 | return $this->currentPage;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/Classes/Event/ModifyCacheLifetimeForPageEvent.php:
--------------------------------------------------------------------------------
1 | cacheLifetime = $cacheLifetime;
39 | }
40 |
41 | public function getCacheLifetime(): int
42 | {
43 | return $this->cacheLifetime;
44 | }
45 |
46 | public function getPageId(): int
47 | {
48 | return $this->pageId;
49 | }
50 |
51 | public function getPageRecord(): array
52 | {
53 | return $this->pageRecord;
54 | }
55 |
56 | public function getRenderingInstructions(): array
57 | {
58 | return $this->renderingInstructions;
59 | }
60 |
61 | public function getContext(): Context
62 | {
63 | return $this->context;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/Classes/Event/ModifyCacheLifetimeForRowEvent.php:
--------------------------------------------------------------------------------
1 | hrefLangs;
35 | }
36 |
37 | public function getRequest(): ServerRequestInterface
38 | {
39 | return $this->request;
40 | }
41 |
42 | /**
43 | * Set the hreflangs. This should be an array in format:
44 | *
45 | * ```
46 | * [
47 | * 'en-US' => 'https://example.com',
48 | * 'nl-NL' => 'https://example.com/nl'
49 | * ]
50 | * ```
51 | *
52 | * @param array $hrefLangs
53 | */
54 | public function setHrefLangs(array $hrefLangs): void
55 | {
56 | $this->hrefLangs = $hrefLangs;
57 | }
58 |
59 | /**
60 | * Add a hreflang tag to the current list of hreflang tags
61 | *
62 | * @param string $languageCode The language of the hreflang tag you would like to add. For example: nl-NL
63 | * @param string $url The URL of the translation. For example: https://example.com/nl
64 | */
65 | public function addHrefLang(string $languageCode, string $url): void
66 | {
67 | $this->hrefLangs[$languageCode] = $url;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/Classes/Event/ModifyPageLinkConfigurationEvent.php:
--------------------------------------------------------------------------------
1 | configuration;
41 | }
42 |
43 | public function setConfiguration(array $configuration): void
44 | {
45 | $this->configuration = $configuration;
46 | }
47 |
48 | public function getLinkDetails(): array
49 | {
50 | return $this->linkDetails;
51 | }
52 |
53 | public function getPage(): array
54 | {
55 | return $this->page;
56 | }
57 |
58 | public function setPage(array $page): void
59 | {
60 | $this->page = $page;
61 | $this->pageWasModified = true;
62 | }
63 |
64 | public function getQueryParameters(): array
65 | {
66 | return $this->queryParameters;
67 | }
68 |
69 | public function setQueryParameters(array $queryParameters): void
70 | {
71 | $this->queryParameters = $queryParameters;
72 | }
73 |
74 | public function getFragment(): string
75 | {
76 | return $this->fragment;
77 | }
78 |
79 | public function setFragment(string $fragment): void
80 | {
81 | $this->fragment = $fragment;
82 | }
83 |
84 | public function pageWasModified(): bool
85 | {
86 | return $this->pageWasModified;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/Classes/Event/ModifyTypoScriptConfigEvent.php:
--------------------------------------------------------------------------------
1 | getAttribute('frontend.typoscript')->getConfigTree(),
36 | * and its array variant $request->getAttribute('frontend.typoscript')->getConfigArray().
37 | *
38 | * Registered listener can *set* a modified setup config AST. Note the TypoScript AST
39 | * structure is still marked @internal within v13 core and may change later,
40 | * using the event to *write* different 'config' data is thus still a bit risky.
41 | */
42 | final class ModifyTypoScriptConfigEvent
43 | {
44 | public function __construct(
45 | private readonly ServerRequestInterface $request,
46 | private readonly RootNode $setupTree,
47 | private RootNode $configTree,
48 | ) {}
49 |
50 | public function getRequest(): ServerRequestInterface
51 | {
52 | return $this->request;
53 | }
54 |
55 | public function getSetupTree(): RootNode
56 | {
57 | return $this->setupTree;
58 | }
59 |
60 | public function getConfigTree(): RootNode
61 | {
62 | return $this->configTree;
63 | }
64 |
65 | public function setConfigTree(RootNode $configTree): void
66 | {
67 | $this->configTree = $configTree;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/Classes/Event/ModifyTypoScriptConstantsEvent.php:
--------------------------------------------------------------------------------
1 | constantsAst;
37 | }
38 |
39 | public function setConstantsAst(RootNode $constantsAst): void
40 | {
41 | $this->constantsAst = $constantsAst;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Classes/Event/ShouldUseCachedPageDataIfAvailableEvent.php:
--------------------------------------------------------------------------------
1 | request->getAttribute('frontend.controller');
40 | }
41 |
42 | public function getRequest(): ServerRequestInterface
43 | {
44 | return $this->request;
45 | }
46 |
47 | public function shouldUseCachedPageData(): bool
48 | {
49 | return $this->shouldUseCachedPageData;
50 | }
51 |
52 | public function setShouldUseCachedPageData(bool $shouldUseCachedPageData): void
53 | {
54 | $this->shouldUseCachedPageData = $shouldUseCachedPageData;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/Classes/Exception.php:
--------------------------------------------------------------------------------
1 | requestHandler = $requestHandler;
42 | }
43 |
44 | public function handle(ServerRequestInterface $request): ResponseInterface
45 | {
46 | // Create new request object having applicationType "I am a frontend request" attribute.
47 | $request = $request->withAttribute('applicationType', SystemEnvironmentBuilder::REQUESTTYPE_FE);
48 |
49 | $this->initializeContext();
50 | return parent::handle($request);
51 | }
52 |
53 | /**
54 | * Initializes the Context used for accessing data and finding out the current state of the application
55 | */
56 | protected function initializeContext(): void
57 | {
58 | $this->context->setAspect(
59 | 'date',
60 | new DateTimeAspect(
61 | DateTimeFactory::createFromTimestamp($GLOBALS['EXEC_TIME'])
62 | )
63 | );
64 | $this->context->setAspect('visibility', new VisibilityAspect());
65 | $this->context->setAspect('workspace', new WorkspaceAspect(0));
66 | $this->context->setAspect('backend.user', new UserAspect(null));
67 | $this->context->setAspect('frontend.user', new UserAspect(null, [0, -1]));
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/Classes/Middleware/CacheTimeout.php:
--------------------------------------------------------------------------------
1 | handle($request);
35 | $config = $request->getAttribute('frontend.controller')?->config['config'] ?? [];
36 | if ($config['cache_clearAtMidnight'] ?? false) {
37 | // @todo: We should probably decide to deprecate or remove cache_clearAtMidnight
38 | // altogether since it is a flawed concept based on server timezone
39 | // "when is midnight?".
40 | $cacheDataCollector = $request->getAttribute('frontend.cache.collector');
41 | $timeOutTime = min($GLOBALS['EXEC_TIME'] + $cacheDataCollector->resolveLifetime(), PHP_INT_MAX);
42 | $midnightTime = mktime(0, 0, 0, (int)date('m', $timeOutTime), (int)date('d', $timeOutTime), (int)date('Y', $timeOutTime));
43 | // If the midnight time of the expire-day is greater than the current time,
44 | // we may set the timeOutTime to the new midnighttime.
45 | if ($midnightTime > $GLOBALS['EXEC_TIME']) {
46 | $cacheDataCollector->restrictMaximumLifetime($midnightTime - $GLOBALS['EXEC_TIME']);
47 | }
48 | }
49 | return $response;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/Classes/Middleware/ContentLengthResponseHeader.php:
--------------------------------------------------------------------------------
1 | handle($request);
42 | $typoScriptConfigArray = $request->getAttribute('frontend.typoscript')->getConfigArray();
43 | if (
44 | (!isset($typoScriptConfigArray['enableContentLengthHeader']) || $typoScriptConfigArray['enableContentLengthHeader'])
45 | && !$this->context->getPropertyFromAspect('backend.user', 'isLoggedIn', false)
46 | && !$this->context->getPropertyFromAspect('workspace', 'isOffline', false)
47 | ) {
48 | $response = $response->withHeader('Content-Length', (string)$response->getBody()->getSize());
49 | }
50 | return $response;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Classes/Middleware/ContentSecurityPolicyReporter.php:
--------------------------------------------------------------------------------
1 | getAttribute('site');
37 | $scope = Scope::frontendSite($site);
38 | if ($this->targetsCspReportUri($scope, $request)) {
39 | $dispositionMap = $this->dispositionMapFactory->buildDispositionMap(
40 | $site instanceof Site ? ($site->getConfiguration()['contentSecurityPolicies'] ?? []) : []
41 | );
42 | // find at least one configured reporting endpoint for the current request
43 | foreach ($dispositionMap->values() as $dispositionConfiguration) {
44 | if ($this->isCspReport($scope, $request, $dispositionConfiguration)) {
45 | $isCspReport = true;
46 | break;
47 | }
48 | }
49 | if (!($isCspReport ?? false)) {
50 | return new HtmlResponse('Submission to CSP reporting endpoint denied', 403);
51 | }
52 | // @todo check/store headers `origin` + `referer`
53 | // @todo create report, then call persist, then dispatch new event
54 | $this->persistCspReport($scope, $request);
55 | return (new Response())->withStatus(201);
56 | }
57 | return $handler->handle($request);
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/Classes/Middleware/EidHandler.php:
--------------------------------------------------------------------------------
1 | getParsedBody()['eID'] ?? $request->getQueryParams()['eID'] ?? null;
46 |
47 | if ($eID === null) {
48 | return $handler->handle($request);
49 | }
50 |
51 | // Remove any output produced until now
52 | ob_clean();
53 |
54 | if (!is_string($eID)) {
55 | return (new Response())->withStatus(400, 'Invalid eID');
56 | }
57 |
58 | $target = $GLOBALS['TYPO3_CONF_VARS']['FE']['eID_include'][$eID] ?? null;
59 | if (empty($target)) {
60 | return (new Response())->withStatus(404, 'eID not registered');
61 | }
62 |
63 | $request = $request->withAttribute('target', $target);
64 | return $this->dispatcher->dispatch($request);
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/Classes/Middleware/MaintenanceMode.php:
--------------------------------------------------------------------------------
1 | getAttribute('normalizedParams')->getRemoteAddress(),
45 | $GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask']
46 | )
47 | ) {
48 | return GeneralUtility::makeInstance(ErrorController::class)->unavailableAction($request, 'This page is temporarily unavailable.');
49 | }
50 | // Continue the regular stack if no maintenance mode is active
51 | return $handler->handle($request);
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Classes/Middleware/OutputCompression.php:
--------------------------------------------------------------------------------
1 | initializeOutputCompression();
44 | return $handler->handle($request);
45 | }
46 |
47 | /**
48 | * Initialize output compression if configured
49 | */
50 | protected function initializeOutputCompression(): void
51 | {
52 | if ($GLOBALS['TYPO3_CONF_VARS']['FE']['compressionLevel'] && extension_loaded('zlib')) {
53 | if (MathUtility::canBeInterpretedAsInteger($GLOBALS['TYPO3_CONF_VARS']['FE']['compressionLevel'])) {
54 | @ini_set('zlib.output_compression_level', (string)$GLOBALS['TYPO3_CONF_VARS']['FE']['compressionLevel']);
55 | }
56 | ob_start([GeneralUtility::makeInstance(CompressionUtility::class), 'compressionOutputHandler']);
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/Classes/Middleware/SiteResolver.php:
--------------------------------------------------------------------------------
1 | matcher->matchRequest($request);
55 |
56 | $site = $routeResult->getSite();
57 | if ($site instanceof Site && $site->invalidSets !== []) {
58 | $invalidSets = implode(', ', array_keys($site->invalidSets));
59 | $this->logger->error('Site {identifier} depends on unavailable sets: {invalidSets}', [
60 | 'identifier' => $site->getIdentifier(),
61 | 'invalidSets' => $invalidSets,
62 | ]);
63 | return $this->errorController->internalErrorAction(
64 | $request,
65 | sprintf(
66 | 'Site %s depends on unavailable sets: %s',
67 | $site->getIdentifier(),
68 | $invalidSets,
69 | ),
70 | ['code' => PageAccessFailureReasons::INVALID_SITE_SETS]
71 | );
72 | }
73 |
74 | $request = $request->withAttribute('site', $site);
75 | $request = $request->withAttribute('language', $routeResult->getLanguage());
76 | $request = $request->withAttribute('routing', $routeResult);
77 | if ($routeResult->getLanguage() instanceof SiteLanguage) {
78 | Locales::setSystemLocaleFromSiteLanguage($routeResult->getLanguage());
79 | }
80 | return $handler->handle($request);
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/Classes/Middleware/TimeTrackerInitialization.php:
--------------------------------------------------------------------------------
1 | isBackendUserCookieSet($request);
45 | $this->timeTracker->setEnabled($timeTrackingEnabled);
46 | $this->timeTracker->start(microtime(true));
47 | $this->timeTracker->push('');
48 |
49 | $response = $handler->handle($request);
50 |
51 | // Finish time tracking
52 | $this->timeTracker->pull();
53 | $this->timeTracker->finish();
54 |
55 | if ($this->isDebugModeEnabled()) {
56 | return $response->withHeader('X-TYPO3-Parsetime', $this->timeTracker->getParseTime() . 'ms');
57 | }
58 | return $response;
59 | }
60 |
61 | /**
62 | * This middleware is run pretty early in the FE chain to initialize correctly.
63 | * It however should only add the response header if debugging is enabled in TypoScript 'config',
64 | * which is not available in the incoming Request, yet.
65 | * It thus listens on the AfterTypoScriptDeterminedEvent to set $this->isDebugEnabledInTypoScriptConfig.
66 | */
67 | #[AsEventListener('typo3-frontend/timetracker-init-middleware')]
68 | public function typoScriptDeterminedListener(AfterTypoScriptDeterminedEvent $event): void
69 | {
70 | $typoScriptConfig = $event->getFrontendTypoScript()->getConfigArray();
71 | if (!empty($typoScriptConfig['debug'] ?? false)) {
72 | $this->isDebugEnabledInTypoScriptConfig = true;
73 | }
74 | }
75 |
76 | protected function isBackendUserCookieSet(ServerRequestInterface $request): bool
77 | {
78 | $configuredCookieName = trim($GLOBALS['TYPO3_CONF_VARS']['BE']['cookieName']) ?: 'be_typo_user';
79 | return !empty($request->getCookieParams()[$configuredCookieName]);
80 | }
81 |
82 | protected function isDebugModeEnabled(): bool
83 | {
84 | if ($this->isDebugEnabledInTypoScriptConfig) {
85 | return true;
86 | }
87 | return !empty($GLOBALS['TYPO3_CONF_VARS']['FE']['debug']);
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/Classes/Page/PageInformationCreationFailedException.php:
--------------------------------------------------------------------------------
1 | code = $code;
37 | }
38 |
39 | public function getResponse(): ResponseInterface
40 | {
41 | return $this->response;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Classes/Resource/PublicUrlPrefixer.php:
--------------------------------------------------------------------------------
1 | getCurrentFrontendController();
39 | if (self::$isProcessingUrl || !$controller) {
40 | return;
41 | }
42 | $resource = $event->getResource();
43 | if (!$this->isLocalResource($resource)) {
44 | return;
45 | }
46 |
47 | // Before calling getPublicUrl, we set the static property to true to avoid to be called in a loop
48 | self::$isProcessingUrl = true;
49 | try {
50 | $resource = $event->getResource();
51 | $originalUrl = $event->getStorage()->getPublicUrl($resource);
52 | if (!$originalUrl || PathUtility::hasProtocolAndScheme($originalUrl)) {
53 | return;
54 | }
55 | $event->setPublicUrl($controller->absRefPrefix . $originalUrl);
56 | } finally {
57 | self::$isProcessingUrl = false;
58 | }
59 | }
60 |
61 | private function isLocalResource(ResourceInterface $resource): bool
62 | {
63 | return $resource->getStorage()->getDriverType() === 'Local';
64 | }
65 |
66 | private function getCurrentFrontendController(): ?TypoScriptFrontendController
67 | {
68 | return $GLOBALS['TSFE'] ?? null;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/Classes/ServiceProvider.php:
--------------------------------------------------------------------------------
1 | self::getApplication(...),
50 | 'frontend.middlewares' => self::getFrontendMiddlewares(...),
51 | ];
52 | }
53 |
54 | public function getExtensions(): array
55 | {
56 | return [
57 | Http\RequestHandler::class => self::provideFallbackRequestHandler(...),
58 | ] + parent::getExtensions();
59 | }
60 |
61 | public static function getApplication(ContainerInterface $container): Http\Application
62 | {
63 | $requestHandler = new MiddlewareDispatcher(
64 | $container->get(Http\RequestHandler::class),
65 | $container->get('frontend.middlewares'),
66 | $container
67 | );
68 |
69 | return self::new($container, Http\Application::class, [
70 | $requestHandler,
71 | $container->get(Context::class),
72 | ]);
73 | }
74 |
75 | public static function provideFallbackRequestHandler(
76 | ContainerInterface $container,
77 | ?RequestHandlerInterface $requestHandler = null
78 | ): RequestHandlerInterface {
79 | // Provide fallback request handler instace for the case where the system is not installed yet (that means when we run without symfony DI).
80 | // This request handler is intended to be never executed, as the frontend application will perform an early redirect to the install tool.
81 | return $requestHandler ?? new class () implements RequestHandlerInterface {
82 | public function handle(ServerRequestInterface $request): ResponseInterface
83 | {
84 | throw new \RuntimeException('not implemented', 1689684150);
85 | }
86 | };
87 | }
88 |
89 | /**
90 | * @throws InvalidDataException
91 | * @throws CoreException
92 | */
93 | public static function getFrontendMiddlewares(ContainerInterface $container): \ArrayObject
94 | {
95 | return new \ArrayObject($container->get(MiddlewareStackResolver::class)->resolve('frontend'));
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/Classes/Typolink/ExternalUrlLinkBuilder.php:
--------------------------------------------------------------------------------
1 | forceAbsoluteUrl($url, $conf);
34 | $fallbackTarget = str_starts_with($url, '/') && !str_starts_with($url, '//') ? 'target' : 'extTarget';
35 |
36 | $linkText = $this->encodeFallbackLinkTextIfLinkTextIsEmpty($linkText, $url);
37 | return (new LinkResult(LinkService::TYPE_URL, (string)$url))
38 | ->withLinkConfiguration($conf)
39 | ->withTarget(
40 | $target ?: $this->resolveTargetAttribute($conf, $fallbackTarget),
41 | )
42 | ->withLinkText($linkText);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Classes/Typolink/FileOrFolderLinkBuilder.php:
--------------------------------------------------------------------------------
1 | getPublicUrl();
41 | if ($linkLocation === null) {
42 | // set the linkLocation to an empty string if null,
43 | // so it does not collide with the various string functions
44 | $linkLocation = '';
45 | }
46 | // Setting title if blank value to link
47 | $linkText = $this->encodeFallbackLinkTextIfLinkTextIsEmpty($linkText, rawurldecode($linkLocation));
48 | $url = $linkLocation;
49 | if (!empty($linkDetails['fragment'])) {
50 | $url .= '#' . $linkDetails['fragment'];
51 | }
52 | return (new LinkResult($linkDetails['type'], $this->forceAbsoluteUrl($url, $conf)))
53 | ->withLinkConfiguration($conf)
54 | ->withTarget($target ?: $this->resolveTargetAttribute($conf, 'fileTarget'))
55 | ->withLinkText($linkText);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Classes/Typolink/LegacyLinkBuilder.php:
--------------------------------------------------------------------------------
1 | getTypoScriptFrontendController();
31 | if ($linkDetails['file'] ?? false) {
32 | $linkDetails['type'] = LinkService::TYPE_FILE;
33 | $linkLocation = $linkDetails['file'];
34 | // Setting title if blank value to link
35 | $linkText = $this->encodeFallbackLinkTextIfLinkTextIsEmpty($linkText, rawurldecode($linkLocation));
36 | $linkLocation = (!str_starts_with($linkLocation, '/') ? $tsfe->absRefPrefix : '') . $linkLocation;
37 | $url = $linkLocation;
38 | $url = $this->forceAbsoluteUrl($url, $conf);
39 | $target = $target ?: $this->resolveTargetAttribute($conf, 'fileTarget');
40 | } elseif ($linkDetails['url'] ?? false) {
41 | $linkDetails['type'] = LinkService::TYPE_URL;
42 | $target = $target ?: $this->resolveTargetAttribute($conf, 'extTarget');
43 | $linkText = $this->encodeFallbackLinkTextIfLinkTextIsEmpty($linkText, $linkDetails['url']);
44 | $url = $linkDetails['url'];
45 | } else {
46 | throw new UnableToLinkException('Unknown link detected, so ' . $linkText . ' was not linked.', 1490990031, null, $linkText);
47 | }
48 | return (new LinkResult((string)$linkDetails['type'], (string)$url))->withTarget($target)->withLinkConfiguration($conf)->withLinkText($linkText);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Classes/Typolink/LinkResultInterface.php:
--------------------------------------------------------------------------------
1 | withLinkConfiguration($conf)->withLinkText($linkText);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Classes/Typolink/UnableToLinkException.php:
--------------------------------------------------------------------------------
1 | linkText;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Classes/Utility/CanonicalizationUtility.php:
--------------------------------------------------------------------------------
1 | getQueryParams() : [];
45 | $GET['id'] = $pageId;
46 |
47 | $queryString = HttpUtility::buildQueryString($GET, '&');
48 | $cHashArray = $cacheHashCalculator->getRelevantParameters($queryString);
49 |
50 | // By exploding the earlier imploded array, we get the flat array with URL params
51 | $urlParameters = GeneralUtility::explodeUrl2Array($queryString);
52 |
53 | $paramsToExclude = array_keys(
54 | array_diff(
55 | $urlParameters,
56 | $cHashArray
57 | )
58 | );
59 |
60 | return array_diff($paramsToExclude, $additionalCanonicalizedUrlParameters);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Classes/Utility/CompressionUtility.php:
--------------------------------------------------------------------------------
1 | contentLength += strlen($outputBuffer);
54 | // Check if this was the last content chunk
55 | if (0 != ($mode & PHP_OUTPUT_HANDLER_END)) {
56 | // Check if we have content-length header
57 | foreach (headers_list() as $header) {
58 | if (strncasecmp('Content-length:', $header, 15) == 0) {
59 | header('Content-length: ' . $this->contentLength);
60 | break;
61 | }
62 | }
63 | }
64 | }
65 | return $outputBuffer;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Configuration/ContentSecurityPolicies.php:
--------------------------------------------------------------------------------
1 | ` - but NOT having the possibility to use any other assets/files/URIs)
25 | new Mutation(MutationMode::Set, Directive::StyleSrcAttr, SourceKeyword::unsafeInline),
26 | // allow `data:` images
27 | new Mutation(MutationMode::Extend, Directive::ImgSrc, SourceScheme::data),
28 | // limits `` element to be use just for same-origin URIs
29 | new Mutation(MutationMode::Set, Directive::BaseUri, SourceKeyword::self),
30 |
31 | // Allows to fetch media assets from YouTube and Vimeo and their associated CDNs,
32 | // to be embedded in an `