Skip to content

Commit

Permalink
[!!!][FEATURE] Automatically register element browsers via service co…
Browse files Browse the repository at this point in the history
…nfiguration

Element browsers are now automatically tagged and registered,
based on the implemented `ElementBrowserInterface`, using the
autoconfiguration feature from the DI container.

The previous registration via
`$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ElementBrowsers']`
has been removed.

Additionally, to be able to use autoconfiguration, the identifier
of a element browser has to be provided by the service directly
using the now required :php:`getIdentifier()` method.

Resolves: #97188
Releases: main
Change-Id: I8fa782331b3226e651c239dfc5d131f3a0a46893
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/73973
Tested-by: core-ci <typo3@b13.com>
Tested-by: Stefan Bürk <stefan@buerk.tech>
Tested-by: Benni Mack <benni@typo3.org>
Tested-by: Oliver Bartsch <bo@cedev.de>
Reviewed-by: Stefan Bürk <stefan@buerk.tech>
Reviewed-by: Benni Mack <benni@typo3.org>
Reviewed-by: Oliver Bartsch <bo@cedev.de>
  • Loading branch information
o-ba committed Mar 23, 2022
1 parent dd1d978 commit 6c7852c
Show file tree
Hide file tree
Showing 15 changed files with 349 additions and 36 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
.. include:: ../../Includes.txt

======================================================================
Breaking: #97188 - Register element browsers via service configuration
======================================================================

See :issue:`97188`

Description
===========

The `element browsers` in EXT:recordlist are now registered via service
configuration, see the :doc:`feature changelog <Feature-97188-NewRegistrationForElementBrowsers>`.
Therefore the registration via
:php:`$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ElementBrowsers']`
has been removed.

Additionally, to be able to use autoconfiguration, the `element browser
identifier has to be provided by the service directly using the
:php:`getIdentifier()` method, which is now required by the
:php:`ElementBrowserInterface`.

In case a custom `element browser` extends
:php:`\TYPO3\CMS\Recordlist\Browser\AbstractElementBrowser`,
only the class property `$identifier` has to be set, e.g.
:php:`protected string $identifier = 'my_browser';`.

Impact
======

Registration of custom `element browsers` via
:php:`$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ElementBrowsers']`
is not evaluated anymore.

The :php:`ElementBrowserInterface` is extended for
:php:`public function getIdentifier(): string`.


Affected Installations
======================

All TYPO3 installations using the old registration.

All TYPO3 installations with custom `element browsers`, not implementing
:php:`public function getIdentifier()`.

Migration
=========

Remove :php:`$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ElementBrowsers']`
from your :file:`ext_localconf.php` file.

If :yaml:`autoconfigure` is not enabled in your :file:`Configuration/Services.(yaml|php)`,
add the tag :yaml:`recordlist.elementbrowser` manually to your `element browser` service.

.. code-block:: yaml
Vendor\Extension\Recordlist\MyBrowser:
tags:
- name: recordlist.elementbrowser
Additionally, make sure to either implement
:php:`public function getIdentifier(): string` or, in case your `element browser`
extends :php:`AbstractElementBrowser`, to set the `$identifier` class property.

.. index:: Backend, LocalConfiguration, PHP-API, FullyScanned, ext:recordlist
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
.. include:: ../../Includes.txt

=======================================================
Feature: #97188 - New registration for element browsers
=======================================================

See :issue:`97188`

Description
===========

The system extension `recordlist` provides different `element browsers`,
such as the "File browser" or the "Database browser" to select files
and records in e.g. FormEngine fields. Extension authors are able to
register their own browsers. This was previously done, using global
configuration.

However, since all `element browsers` have to implement the
:php:`ElementBrowserInterface`, this fact is now used to automatically
register the `element browsers`, based on the interface, if
:yaml:`autoconfigure` is enabled in :file:`Services.yaml`. Alternatively,
one can manually tag a custom `element browsers` with the
:yaml:`recordlist.elementbrowser` tag (See section "Migration" in the
:doc:`breaking changelog <Breaking-97188-RegisterElementBrowsersViaServiceConfiguration>`).

Due to the autoconfiguration, the identifier has to be provided by the
class directly, using the now required :php:`getIdentifier()` method.
When extending :php:`\TYPO3\CMS\Recordlist\Browser\AbstractElementBrowser`
it's sufficient to set the `$identifier` class property.

Impact
======

`element browsers` are now automatically registered through the service
configuration, based on the implemented interface.

.. index:: Backend, LocalConfiguration, PHP-API, ext:recordlist
Original file line number Diff line number Diff line change
Expand Up @@ -655,4 +655,9 @@
'Breaking-97187-RemovedHookForModifyingLinkExplanation.rst',
],
],
'$GLOBALS[\'TYPO3_CONF_VARS\'][\'SC_OPTIONS\'][\'ElementBrowsers\']' => [
'restFiles' => [
'Breaking-97188-RegisterElementBrowsersViaServiceConfiguration.rst',
],
],
];
13 changes: 13 additions & 0 deletions typo3/sysext/recordlist/Classes/Browser/AbstractElementBrowser.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ abstract class AbstractElementBrowser
{
use PageRendererBackendSetupTrait;

/**
* The element browsers unique identifier
*/
protected string $identifier = '';

/**
* URL of current request
*
Expand Down Expand Up @@ -92,6 +97,14 @@ protected function initialize()
$this->initVariables();
}

/**
* Returns the identifier for the browser
*/
public function getIdentifier(): string
{
return $this->identifier;
}

/**
* Sets the script url depending on being a module or script request
*/
Expand Down
2 changes: 2 additions & 0 deletions typo3/sysext/recordlist/Classes/Browser/DatabaseBrowser.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
*/
class DatabaseBrowser extends AbstractElementBrowser implements ElementBrowserInterface, LinkParameterProviderInterface
{
protected string $identifier = 'db';

/**
* When you click a page title/expand icon to see the content of a certain page, this
* value will contain the ID of the expanded page.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

/*
* This file is part of the TYPO3 CMS project.
*
Expand All @@ -17,6 +19,11 @@

interface ElementBrowserInterface
{
/**
* Returns the unique identifier of the element browser
*/
public function getIdentifier(): string;

/**
* @return string HTML content
*/
Expand Down
80 changes: 80 additions & 0 deletions typo3/sysext/recordlist/Classes/Browser/ElementBrowserRegistry.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?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\Recordlist\Browser;

/**
* Registry for element browsers. The registry receives all services, tagged with "recordlist.elementbrowser".
* The tagging of element browsers is automatically done based on the implemented ElementBrowserInterface.
*
* @internal
*/
class ElementBrowserRegistry
{
/**
* @var ElementBrowserInterface[]
*/
private array $elementBrowsers = [];

public function __construct(iterable $elementBrowsers)
{
foreach ($elementBrowsers as $elementBrowser) {
if (!($elementBrowser instanceof ElementBrowserInterface)) {
continue;
}

$identifier = $elementBrowser->getIdentifier();
if ($identifier === '') {
throw new \InvalidArgumentException('Identifier for element browser ' . get_class($elementBrowser) . ' is empty.', 1647241084);
}
if (isset($this->elementBrowsers[$identifier])) {
throw new \InvalidArgumentException('Element browser with identifier ' . $identifier . ' is already registered.', 1647241085);
}
$this->elementBrowsers[$identifier] = $elementBrowser;
}
}

/**
* Whether a registered element browser exists for the identifier
*/
public function hasElementBrowser(string $identifier): bool
{
return isset($this->elementBrowsers[$identifier]);
}

/**
* Get registered element browser by identifier
*/
public function getElementBrowser(string $identifier): ElementBrowserInterface
{
if (!$this->hasElementBrowser($identifier)) {
throw new \UnexpectedValueException('Element browser with identifier ' . $identifier . ' is not registered.', 1647241086);
}

return $this->elementBrowsers[$identifier];
}

/**
* Get all registered element browsers
*
* @return ElementBrowserInterface[]
*/
public function getElementBrowsers(): array
{
return $this->elementBrowsers;
}
}
2 changes: 2 additions & 0 deletions typo3/sysext/recordlist/Classes/Browser/FileBrowser.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
*/
class FileBrowser extends AbstractElementBrowser implements ElementBrowserInterface, LinkParameterProviderInterface
{
protected string $identifier = 'file';

/**
* When you click a folder name/expand icon to see the content of a certain file folder,
* this value will contain the path of the expanded file folder.
Expand Down
2 changes: 2 additions & 0 deletions typo3/sysext/recordlist/Classes/Browser/FolderBrowser.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
*/
class FolderBrowser extends AbstractElementBrowser implements ElementBrowserInterface, LinkParameterProviderInterface
{
protected string $identifier = 'folder';

/**
* When you click a folder name/expand icon to see the content of a certain file folder,
* this value will contain the path of the expanded file folder.
Expand Down
4 changes: 4 additions & 0 deletions typo3/sysext/recordlist/Classes/Browser/RecordBrowser.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@
*/
class RecordBrowser extends DatabaseBrowser
{
// @todo: Prevent RecordBrowser from overwriting the DatabaseBrowser registration.
// Remove as soon as the misuse of extending DatabaseBrowser has been resolved.
protected string $identifier = 'record';

protected array $urlParameters = [];

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Http\HtmlResponse;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Recordlist\Browser\ElementBrowserInterface;
use TYPO3\CMS\Recordlist\Browser\ElementBrowserRegistry;

/**
* Script class for the Element Browser window.
Expand All @@ -44,6 +43,10 @@ class ElementBrowserController
*/
protected string $mode = '';

public function __construct(protected readonly ElementBrowserRegistry $elementBrowserRegistry)
{
}

/**
* Injects the request object for the current request or sub-request
* As this controller goes only through the main() method, it is rather simple for now
Expand All @@ -65,7 +68,7 @@ public function mainAction(ServerRequestInterface $request): ResponseInterface
*/
protected function main(ServerRequestInterface $request)
{
$browser = $this->getElementBrowserInstance();
$browser = $this->elementBrowserRegistry->getElementBrowser($this->mode);
if (is_callable([$browser, 'setRequest'])) {
$browser->setRequest($request);
}
Expand All @@ -78,22 +81,6 @@ protected function main(ServerRequestInterface $request)
return $browser->render();
}

/**
* Get instance of the actual element browser
*
* @return ElementBrowserInterface
* @throws \UnexpectedValueException
*/
protected function getElementBrowserInstance()
{
$className = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ElementBrowsers'][$this->mode];
$browser = GeneralUtility::makeInstance($className);
if (!$browser instanceof ElementBrowserInterface) {
throw new \UnexpectedValueException('The specified element browser "' . $className . '" does not implement the required ElementBrowserInterface', 1442763890);
}
return $browser;
}

protected function getLanguageService(): LanguageService
{
return $GLOBALS['LANG'];
Expand Down
12 changes: 12 additions & 0 deletions typo3/sysext/recordlist/Configuration/Services.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);
namespace TYPO3\CMS\Backend\RecordList;

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use TYPO3\CMS\Recordlist\Browser\ElementBrowserInterface;

return static function (ContainerConfigurator $container, ContainerBuilder $containerBuilder) {
$containerBuilder->registerForAutoconfiguration(ElementBrowserInterface::class)->addTag('recordlist.elementbrowser');
};
8 changes: 5 additions & 3 deletions typo3/sysext/recordlist/Configuration/Services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,12 @@ services:

TYPO3\CMS\Recordlist\Browser\DatabaseBrowser:
shared: false
public: true

TYPO3\CMS\Recordlist\Browser\FileBrowser:
shared: false
public: true

TYPO3\CMS\Recordlist\Browser\FolderBrowser:
shared: false
public: true

TYPO3\CMS\Recordlist\Browser\RecordBrowser:
shared: false
Expand All @@ -39,3 +36,8 @@ services:
TYPO3\CMS\Recordlist\View\RecordSearchBoxComponent:
shared: false
public: true

# Element browser registry
TYPO3\CMS\Recordlist\Browser\ElementBrowserRegistry:
arguments:
- !tagged_iterator recordlist.elementbrowser
Loading

0 comments on commit 6c7852c

Please sign in to comment.