TYPO3 v15 dev-main snapshot ()
This commit is contained in:
@@ -0,0 +1,207 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Backend\Template\Components;
|
||||
|
||||
use Psr\EventDispatcher\EventDispatcherInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
|
||||
use TYPO3\CMS\Backend\Template\Components\Buttons\ButtonInterface;
|
||||
use TYPO3\CMS\Backend\Template\Components\Buttons\PositionInterface;
|
||||
|
||||
/**
|
||||
* Container for managing buttons in the backend module document header.
|
||||
* The ButtonBar organizes buttons into two positions (left and right) with multiple groups,
|
||||
* allowing precise control over button placement and visual grouping.
|
||||
*
|
||||
* Buttons are organized in a three-level structure:
|
||||
* 1. Position: LEFT or RIGHT side of the header
|
||||
* 2. Group: Numerical groups within each position (1, 2, 3, etc.)
|
||||
* 3. Individual buttons within each group
|
||||
*
|
||||
* Groups are rendered with visual spacing between them, allowing logical grouping
|
||||
* of related actions. Lower group numbers appear first (left-to-right, or top-to-bottom on mobile).
|
||||
*
|
||||
* Example - Using pre-configured buttons from ComponentFactory:
|
||||
*
|
||||
* ```
|
||||
* public function __construct(
|
||||
* protected readonly ComponentFactory $componentFactory,
|
||||
* ) {}
|
||||
*
|
||||
* public function myAction(): ResponseInterface
|
||||
* {
|
||||
* $buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
|
||||
*
|
||||
* // Use pre-configured save button
|
||||
* $saveButton = $this->componentFactory->createSaveButton('editform');
|
||||
* $buttonBar->addButton($saveButton, ButtonBar::BUTTON_POSITION_LEFT, 1);
|
||||
*
|
||||
* // Use pre-configured back button
|
||||
* $backButton = $this->componentFactory->createBackButton($returnUrl);
|
||||
* $buttonBar->addButton($backButton, ButtonBar::BUTTON_POSITION_LEFT, 2);
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Example - Creating custom buttons with ComponentFactory:
|
||||
*
|
||||
* ```
|
||||
* public function __construct(
|
||||
* protected readonly ComponentFactory $componentFactory,
|
||||
* ) {}
|
||||
*
|
||||
* public function myAction(): ResponseInterface
|
||||
* {
|
||||
* $buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
|
||||
*
|
||||
* // Create custom link button
|
||||
* $customButton = $this->componentFactory->createLinkButton()
|
||||
* ->setHref('/custom-action')
|
||||
* ->setTitle('Custom Action')
|
||||
* ->setIcon($iconFactory->getIcon('actions-custom'));
|
||||
* $buttonBar->addButton($customButton, ButtonBar::BUTTON_POSITION_LEFT, 1);
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Example - Buttons with automatic positioning:
|
||||
*
|
||||
* ```
|
||||
* // ShortcutButton implements PositionInterface and positions itself automatically
|
||||
* $shortcutButton = $this->componentFactory->createShortcutButton()
|
||||
* ->setRouteIdentifier('my_module')
|
||||
* ->setDisplayName('My Module');
|
||||
* $buttonBar->addButton($shortcutButton); // Position and group are automatic
|
||||
* ```
|
||||
*
|
||||
* Example - Dropdown button:
|
||||
*
|
||||
* ```
|
||||
* $dropdownButton = $this->componentFactory->createDropDownButton()
|
||||
* ->setLabel('Actions')
|
||||
* ->setIcon($iconFactory->getIcon('actions-menu'));
|
||||
*
|
||||
* $item1 = $this->componentFactory->createDropDownItem()
|
||||
* ->setLabel('Edit')
|
||||
* ->setHref('/edit');
|
||||
* $dropdownButton->addItem($item1);
|
||||
*
|
||||
* $item2 = $this->componentFactory->createDropDownItem()
|
||||
* ->setLabel('Delete')
|
||||
* ->setHref('/delete');
|
||||
* $dropdownButton->addItem($item2);
|
||||
*
|
||||
* $buttonBar->addButton($dropdownButton, ButtonBar::BUTTON_POSITION_RIGHT, 1);
|
||||
* ```
|
||||
*
|
||||
* @phpstan-type Buttons array<self::BUTTON_POSITION_*, array<int, list<ButtonInterface>>>
|
||||
*/
|
||||
#[Autoconfigure(public: true)]
|
||||
class ButtonBar
|
||||
{
|
||||
/**
|
||||
* Position constant for left side of the button bar
|
||||
*/
|
||||
public const BUTTON_POSITION_LEFT = 'left';
|
||||
|
||||
/**
|
||||
* Position constant for right side of the button bar
|
||||
*/
|
||||
public const BUTTON_POSITION_RIGHT = 'right';
|
||||
|
||||
/**
|
||||
* Internal array of all registered buttons
|
||||
*
|
||||
* @var Buttons
|
||||
*/
|
||||
protected array $buttons = [];
|
||||
|
||||
public function __construct(
|
||||
protected readonly EventDispatcherInterface $eventDispatcher,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Add a new button
|
||||
*
|
||||
* Buttons implementing PositionInterface will automatically use their own
|
||||
* predefined position and group, ignoring the $buttonPosition and $buttonGroup
|
||||
* parameters. This ensures buttons like ShortcutButton always appear in their
|
||||
* designated location.
|
||||
*
|
||||
* @param ButtonInterface $button The Button Object to add
|
||||
* @param self::BUTTON_POSITION_* $buttonPosition Position of the button (left/right). Ignored if button implements PositionInterface.
|
||||
* @param int $buttonGroup Buttongroup of the button. Ignored if button implements PositionInterface.
|
||||
*
|
||||
* @throws \InvalidArgumentException In case a button is not valid
|
||||
*/
|
||||
public function addButton(
|
||||
ButtonInterface $button,
|
||||
string $buttonPosition = self::BUTTON_POSITION_LEFT,
|
||||
int $buttonGroup = 1
|
||||
): static {
|
||||
if (!$button->isValid()) {
|
||||
throw new \InvalidArgumentException('Button "' . $button->getType() . '" is not valid', 1441706370);
|
||||
}
|
||||
// Buttons implementing PositionInterface define their own position and group
|
||||
if ($button instanceof PositionInterface) {
|
||||
$buttonPosition = $button->getPosition();
|
||||
$buttonGroup = $button->getGroup();
|
||||
}
|
||||
// We make the button immutable here
|
||||
$this->buttons[$buttonPosition][$buttonGroup][] = clone $button;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an associative array of all buttons in the form of
|
||||
* ButtonPosition > ButtonGroup > Button
|
||||
*/
|
||||
public function getButtons(ServerRequestInterface $request): array
|
||||
{
|
||||
// here we need to call the sorting methods and stuff.
|
||||
foreach ($this->buttons as $position => $_) {
|
||||
ksort($this->buttons[$position]);
|
||||
}
|
||||
|
||||
// Dispatch event for manipulating the docHeaderButtons
|
||||
$this->buttons = $this->eventDispatcher->dispatch(new ModifyButtonBarEvent($this->buttons, $this, $request))->getButtons();
|
||||
|
||||
return $this->buttons;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a button of the specified type has been added to the button bar.
|
||||
*
|
||||
* This is primarily used for backwards compatibility detection to prevent
|
||||
* duplicate automatic buttons when controllers manually add them.
|
||||
*
|
||||
* @param class-string<ButtonInterface> $buttonClassName Fully qualified button class name
|
||||
* @return bool True if a button of this type exists, false otherwise
|
||||
*/
|
||||
public function hasButtonOfType(string $buttonClassName): bool
|
||||
{
|
||||
foreach ($this->buttons as $groups) {
|
||||
foreach ($groups as $groupButtons) {
|
||||
foreach ($groupButtons as $button) {
|
||||
if ($button instanceof $buttonClassName) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user