Photo of Torben Hansen

A TechBlog by Torben Hansen


Freelance Full Stack Web Developer located in Germany.
I create web applications mainly using TYPO3, PHP, Python and JavaScript.
Home Archive Tags

Extending an existing extbase backend module with a custom action in TYPO3 v12+

One part I really like about TYPO3 is its extendability. The TYPO3 core and its APIs contain many PSR-14 events and hooks, which developers can use to customize or extend the functionality of the system. My TYPO3 extension sf_event_mgt has an extbase based backend module, which allows editors to manage events and event registrations. It is for example possible to export a CSV list of participants or to notify the participants of an event by email. Sometimes, the included features of the backend module however do not cover all customer demands. In such situations, I usually extend the existing backend module with the needed features, so the customer do not have to use 2 backend modules to manage events.

πŸ”₯πŸ”₯πŸ”₯ Never modify code in a public extension for your project πŸ”₯πŸ”₯πŸ”₯

You should never add custom code directly in local instances of public TYPO3 extensions, since you will end up in a state, where it will be hard or impossible to update the extension. This is really no fun in terms of TYPO3 major updates and also pretty bad in case of a security issue in the public extension, where you then manually have to apply the security patch.

Adding a new action to a backend module of an existing extension

In the following step-by-step guide, I will show how extend the existing extbase backend module of an extension with a custom action. My TYPO3 extension sf_event_mgt will be the extension where the backend module is extended.

1. Create a new plain extension

First step is to create a new extension, which will hold all code for the required functionality. Ensure, that the new extension depends on the extension you extend. To do so, add the β€œmain” extension as dependency in composer.json file (TYPO3 composer mode) or ext_emconf.php file (TYPO3 classic mode). This will ensure, that the extending extension is loaded after the extension you extend. In this example, I will use sf_event_mgt_extend as extensions name.

2. Define template overrides for backend module

To override templates of a TYPO3 backend module, it is required to define template overrides. Since TYPO3 12, this is achieved using TSconfig (see https://docs.typo3.org/c/typo3/cms-core/main/en-us/Changelog/12.0/Feature-96812-OverrideBackendTemplatesWithTSconfig.html).

templates.derhansen/sf_event_mgt.20 = derhansen/sf_event_mgt_extend:Resources/Private

The backend module of sf_event_mgt will then also look for template overrides in sf_event_mgt_extend

3. XCLASS the controller of the original backend module

Although XCLASSing is considered risky, it as a nice possiblity to add functionality to an existing class. I personally only use XCLASS to add functionality and never to overwrite something in the XCLASSed class.

In ext_localconf.php, I define the new class for the XLCASSed class.

$GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][\DERHANSEN\SfEventMgt\Controller\AdministrationController::class] = [
    'className' => \Derhansen\SfEventMgtExtend\Controller\AdministrationController::class
];

And in Classes/Controller/AdministrationController.php, I extend the original controller with the custom action as shown below.

<?php

declare(strict_types=1);

namespace Derhansen\SfEventMgtExtend\Controller;

use Psr\Http\Message\ResponseInterface;

class MyAdministrationController extends \DERHANSEN\SfEventMgt\Controller\AdministrationController
{
    public function doSomethingAction(): ResponseInterface
    {
        $variables = [
            'myVariable' => 'Variable content',
        ];

        return $this->initModuleTemplateAndReturnResponse('Administration/DoSomething', $variables);
    }
}

Note, that depending on the extension you extend, it may be, that you have to manually constuct the module template response.

3. Add event listener to register custom action

Next, you have to make the original backend module aware of the new action. To do so, an event lister for the BeforeModuleCreationEvent has to be used, where the controllerActions array of the original backend module controller is extended.

Register the event listener in the Configuration/Services.yaml file of the extending extension as shown below:

Derhansen\SfEventMgtExtend\EventListener\ExtendAdministrationModule:
  tags:
    - name: event.listener
      identifier: 'sf-event-extend/backend/extend-administration-module'

Side note: In TYPO3 v13, you can also use the AsEventListener PHP attribute and skip the registration in Configuration/Services.yaml.

Next, implement the event listener in Classes/EventListener/ExtendAdministrationModule.php as shown below:

<?php

declare(strict_types=1);

namespace Derhansen\SfEventExtend\EventListener;

use DERHANSEN\SfEventMgt\Controller\AdministrationController;
use TYPO3\CMS\Backend\Module\BeforeModuleCreationEvent;

class ExtendAdministrationModule
{
    public function __invoke(BeforeModuleCreationEvent $event): void
    {
        $configuration = $event->getConfiguration();
        if (($configuration['extensionName'] ?? '') === 'SfEventMgt') {
            $configuration['controllerActions'][AdministrationController::class][] = 'doSomething';
            $event->setConfiguration($configuration);
        }
    }
}

4. Create fluid template for new action

Since the new action is pretty simple, I just add a fluid template for the action by adding the file Resources/Private/Templates/Administration/DoSomething.html.

5. Use new action in backend module templates

The new action is now ready to be used in templates. As an example, I extend the ListItem.html partial of the original extension by defining a template override in the extending extension. I add the file Resources/Private/Partials/Administration/ListItem.html and copy the content of the original file of the main extension to it. Next, I add the following new HTML code to the extended partial.

<f:link.action action="doSomething" class="btn btn-default btn-sm" title="Do something">
    <core:icon identifier="actions-list-alternative" size="small" />
</f:link.action>

Finally, the new action can be used in the existing backend module of sf_event_mgt.