Skip to content

Instantly share code, notes, and snippets.

@o-ba
Last active October 14, 2021 14:15
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save o-ba/6a68d687eaf9fa986981e1db7d1c6faf to your computer and use it in GitHub Desktop.
Save o-ba/6a68d687eaf9fa986981e1db7d1c6faf to your computer and use it in GitHub Desktop.
Example code for various TYPO3 v11 features
This Gist contains code examples, related to the talk hold at the TMUG on 11.10.2021.
The slides and further information can be found here: https://www.mtug.de/treffen/veranstaltungen-2021/oktober.html
Note: The Gist contains a couple of additional examples, not shown during the demo.
// The configuration module can be extended by custom providers
// See for reference: https://docs.typo3.org/c/typo3/cms-core/master/en-us/Changelog/11.0/Feature-92929-ExtendableConfigurationModule.html
<?php
declare(strict_types=1);
namespace Vendor\Site;
use TYPO3\CMS\Lowlevel\ConfigurationModuleProvider\AbstractProvider;
class CustomConfigurationProvider extends AbstractProvider
{
/**
* Since we extend from AbstractProvider, we only need to implement getConfiguration(),
* containing the configuration to be displayed as array.
*/
public function getConfiguration(): array
{
return [
'my-key' => [
'my-sub-key' => 'my-value'
]
];
}
}
// The corresponding registration in Services.yaml
lowlevel.configuration.module.provider.customconfiguration:
class: 'Vendor\Site\CustomConfigurationProvider'
tags:
- name: 'lowlevel.configuration.module.provider'
identifier: 'customConfiguration'
label: 'Custom configuration'
after: 'httpMiddlewareStacks'
before: 'siteConfiguration'
// In case we just want to display custom global configuration, core's "GlobalVariableProvider" can be used
lowlevel.configuration.module.provider.customconfiguration:
class: 'TYPO3\CMS\Lowlevel\ConfigurationModuleProvider\GlobalVariableProvider'
tags:
- name: 'lowlevel.configuration.module.provider'
identifier: 'customConfiguration'
label: 'My global var'
globalVariableKey: 'MY_GLOBAL_VAR'
// The above will display the contents of $GLOBALS['MY_GLOBAL_VAR']
// Use of PSR-7 request in a custom content object
// See for reference: https://docs.typo3.org/c/typo3/cms-core/master/en-us/Changelog/11.0/Feature-92984-PSR-7RequestAvailableInFrontendContentObjects.html
<?php
declare(strict_types=1);
namespace Vendor\Site\UserFunctions;
use Psr\Http\Message\ServerRequestInterface;
class CustomContentObject
{
public function render(string $content, array $configuration, ServerRequestInterface $request): string
{
return 'You are on page: ' . $request->getAttribute('normalizedParams')->getRequestUri();
}
}
// Add the content object to your PAGE
page.2 = USER
page.2.userFunc = Vendor\Site\UserFunctions\CustomContentObject->render
// A custom module controller for a TYPO3 backend module, adding the "Link sharing" button
// See for reference: https://docs.typo3.org/c/typo3/cms-core/master/en-us/Changelog/11.3/Feature-93921-SharingBackendLinks.html
<?php
declare(strict_types=1);
namespace Vendor\Site\Backend;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\StreamFactoryInterface;
use TYPO3\CMS\Backend\Template\ModuleTemplateFactory;
class CustomModuleController
{
protected ResponseFactoryInterface $responseFactory;
protected StreamFactoryInterface $streamFactory;
protected ModuleTemplateFactory $moduleTemplateFactory;
public function __construct(
ResponseFactoryInterface $responseFactory,
StreamFactoryInterface $streamFactory,
ModuleTemplateFactory $moduleTemplateFactory
) {
$this->responseFactory = $responseFactory;
$this->streamFactory = $streamFactory;
$this->moduleTemplateFactory = $moduleTemplateFactory;
}
public function handleRequest(ServerRequestInterface $request): ResponseInterface
{
// Creating the moduleTemplate instance via the new ModuleTemplateFactory
$moduleTemplate = $this->moduleTemplateFactory->create($request);
// Assigning content to the moduleTemplate
$moduleTemplate->setContent('My module');
// Retrieving the button bar component
$buttonBar = $moduleTemplate->getDocHeaderComponent()->getButtonBar();
// Adding the "share link" button
$button = $buttonBar->makeShortcutButton()
->setRouteIdentifier('web_custom')
->setDisplayName('Custom Module');
// With ->setCopyUrlToClipboard(false) the sharing button whould not be displayed, but only the bookmark button
// Adding the button to the button bar
$buttonBar->addButton($button);
// Creating the PSR-7 Response, using PRS-17 factories
return $this->responseFactory->createResponse()->withBody(
$this->streamFactory->createStream($moduleTemplate->renderContent())
);
}
}
// Note: Module needs to be added in ext_tables.php
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addModule(
'web',
'custom',
'bottom',
'',
[
'routeTarget' => \Vendor\Site\Backend\CustomModuleController::class . '::handleRequest',
'name' => 'web_custom',
'access' => 'user,group',
'iconIdentifier' => 'tx-site-icon',
'labels' => 'LLL:EXT:site/Resources/Private/Language/locallang_custom_module.xlf',
]
);
// Note: Since we use DI, but the controller is called via GeneralUtility::makeInstance, we need to make it
// public in the service configuration. This can be done by tagging the service with "backend.controller"
Vendor\Site\Backend\CustomModuleController:
tags: [ 'backend.controller' ]
// Registration of icons via Configuration/Icons.php
// See for reference: https://docs.typo3.org/c/typo3/cms-core/master/en-us/Changelog/11.4/Feature-94692-RegisteringIconsViaServiceContainer.html
<?php
declare(strict_types=1);
use TYPO3\CMS\Core\Imaging\IconProvider\SvgIconProvider;
return [
'tx-site-icon' => [
'provider' => SvgIconProvider::class,
'source' => 'EXT:site/Resources/Public/Icons/Extension.svg'
]
];
Various settings for MFA, see for reference: https://docs.typo3.org/c/typo3/cms-core/master/en-us/Changelog/11.1/Feature-93526-MultiFactorAuthentication.html
1. Hide the "mfa" field in the user settings:
setup.fields.mfaProviders.disabled = 1
2. Require MFA being set up (via globals configuration):
$GLOBALS['TYPO3_CONF_VARS']['BE']['requireMfa'] = 0|1|2|3|4
3. Require MFA for users / user groups (user TSconfig):
auth.mfa.required = 1
4. Define a recommended MFA provider (via globals configuration):
$GLOBALS['TYPO3_CONF_VARS]['BE]['recommendedMfaProvider'] = 'provider-identifier'
5. Define a recommended MFA provider for user / user groups (user TSconfig):
auth.mfa.recommendedProvider = totp
6. Disabled providers for users / user groups (user TSconfig):
auth.mfa.disableProviders := addToList(provider-identifier)
// A couple of new PSR-14 events is available. Furthermore is the "event" tag argument not required anymore, since the event
// is automatically determined, based on the corresponding method argument.
// Example Event listener for the page module, using the "stoppable events" feature of PSR-14
<?php
declare(strict_types=1);
namespace Vendor\Site\EventListener;
use TYPO3\CMS\Backend\View\Event\BeforeSectionMarkupGeneratedEvent;
class PageLayoutEventListener
{
public function __invoke(BeforeSectionMarkupGeneratedEvent $event): void
{
$event->setContent('Custom markup for the page module');
$event->setStopRendering(true);
}
public function addAdditionalContent(BeforeSectionMarkupGeneratedEvent $event): void
{
// This will not be added since the method above - executed beforehand - stops rendering
$event->setContent($event->getContent() . ' - Additional content');
}
}
# Registration without "event" tag attribute. Also "method" is only used once, since the first listener uses __invoke()
Vendor\Site\EventListener\PageLayoutEventListener:
tags:
- name: event.listener
identifier: 'before-section-page-layout'
- name: event.listener
identifier: 'before-section-page-layout-2'
method: 'addAdditionalContent'
Use site configuration values in TCA foreign_table_where
See for reference: https://docs.typo3.org/c/typo3/cms-core/master/en-us/Changelog/11.4/Feature-94662-AddPlaceholderForSiteConfigurationInForeignTableWhere.html
// Configuration/TCA/my_table.php
'columns' => [
'label' => 'Content',
'description' => 'Select a content element'
'config' => [
'type' => 'select',
'renderType' => 'selectSingle',
'foreign_table' => 'tt_content',
'foreign_table_where' => 'AND {#tt_content}.{#uid} = ###SITE:settings.contentPid###'
]
]
// config/sites/my_site.yaml
rootPageId: 1
websiteTitle: 'My website'
settings:
contentPid: 11
// Using PSR-7 and PSR-17 in an extbase controller
// See for reference: https://docs.typo3.org/c/typo3/cms-core/master/en-us/Changelog/11.0/Breaking-92502-MakeExtbaseHandlePSR7ResponsesOnly.html
// See for reference: https://docs.typo3.org/c/typo3/cms-core/master/en-us/Changelog/11.0/Feature-92815-IntroduceForwardResponseForExtbase.html
// See for reference: https://docs.typo3.org/c/typo3/cms-core/master/en-us/Changelog/11.3/Feature-94428-ExtbaseRequestImplementsServerRequestInterface.html
<?php
declare(strict_types=1);
namespace Vendor\Site\Controller;
use Vendor\Site\Domain\Repository\StoreRepository;
use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Extbase\Http\ForwardResponse;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
class StoreController extends ActionController
{
protected StoreRepository $storeRepository;
public function __construct(StoreRepository $storeRepository)
{
// Inject a repository
$this->storeRepository = $storeRepository;
}
public function listAction(): ResponseInterface
{
// Fetch records from a repository
$stores = $this->storeRepository->findAll();
// Access PSR-7 request object through ExtbaseRequest, which implements PSR-7
$testParam = $this->request->getQueryParams()['param'] ?? '';
if ($testParam === 'other') {
// Return PSR-7 response, forwarding to another action
return new ForwardResponse('other');
}
// Assign stores to the View
$this->view->assign('stores', $stores);
// Manually create a PSR-7 response using PSR-17 factories
return $this->responseFactory->createResponse()
->withBody($this->streamFactory->createStream($this->view->render()));
// Or, simly use the helper method
// return $this->htmlResponse();
}
protected function initializeOtherAction(): void
{
$this->defaultViewObjectName = JsonView::class;
}
public function otherAction(): ResponseInterface
{
// In this action, we return the first stores' title as JSON
$this->view->assign('value', [
$this->storeRepository->findAll()->getFirst()->getTitle(),
]);
return $this->jsonResponse();
}
}
// Calling the plugin via a custom page type
store = PAGE
store {
typeNum = 100
10 = USER
10 {
userFunc = TYPO3\CMS\Extbase\Core\Bootstrap->run
vendorName = Vendor
extensionName = Site
pluginName = Store
controllerName = Store
}
}
// Configure the plugin in ext_localconf.php
\TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin(
'Site',
'Store',
[
\Vendor\Site\Controller\StoreController::class => 'list,other'
],
[
\Vendor\Site\Controller\StoreController::class => 'list,other'
],
\TYPO3\CMS\Extbase\Utility\ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
);
This file contains links to further code examples in the official changelog
1. TypoScript Condition for page layout
https://docs.typo3.org/c/typo3/cms-core/master/en-us/Changelog/11.0/Feature-88276-TypoScriptConditionForPageLayout.html
2. Deault values in NewRecordViewHelper
https://docs.typo3.org/c/typo3/cms-core/master/en-us/Changelog/11.0/Feature-92462-AddOptionalDefaultValuesArgumentToNewRecordViewHelpers.html
3. Icon overlay in NewContentElementWizard
https://docs.typo3.org/c/typo3/cms-core/master/en-us/Changelog/11.1/Feature-92942-AllowIconOverlayForNewContentElementWizardElements.html
4. Override TCA field description with page TSconfig
https://docs.typo3.org/c/typo3/cms-core/master/en-us/Changelog/11.2/Feature-93794-OverrideTCADescriptionWithTSconfig.html
5. TCA description for palettes
https://docs.typo3.org/c/typo3/cms-core/master/en-us/Changelog/11.3/Feature-89507-AddDescriptionForTCAPalettes.html
6. TCA type "language":
https://docs.typo3.org/c/typo3/cms-core/master/en-us/Changelog/11.2/Feature-57082-NewTCATypeLanguage.html
7. TCA type "category":
https://docs.typo3.org/c/typo3/cms-core/master/en-us/Changelog/11.4/Feature-94622-NewTCATypeCategory.html
Note:
Also have a look at https://docs.typo3.org/c/typo3/cms-core/master/en-us/Changelog/11.4/Feature-95037-NewStartingPointsSettingForFormEngineTreeConfig.html
This could be used like this:
$GLOBALS['TCA']['my_table']['columns']['categories'] = [
'label' => 'categories',
'config' => [
'type' => 'category',
'treeConfig' => [
'startingPoints' => '2,18'
]
]
];
8. Add buttons to the page module docheader, using the "drawItemHook"
https://docs.typo3.org/c/typo3/cms-core/master/en-us/Changelog/11.3/Feature-92358-AddGetModuleTemplateToPageLayoutController.html
# Allowing widgets to be refreshed
# See for reference: https://docs.typo3.org/c/typo3/cms-core/master/en-us/Changelog/11.3/Feature-93210-PossibilityToRefreshDashboardWidgets.html
dashboard.widget.myWidget:
class: 'TYPO3\CMS\Dashboard\Widgets\DoughnutChartWidget'
arguments:
$view: '@dashboard.views.widget'
$dataProvider: '@dashboard.provider.myWidgetProvider'
$options:
refreshAvailable: true
tags:
-
name: dashboard.widget
identifier: 'myWidget'
...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment