TYPO3 v15 dev-main snapshot ()
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,68 @@
|
||||
<?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\RecordList;
|
||||
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
final class DownloadPreset
|
||||
{
|
||||
public function __construct(
|
||||
private readonly string $label,
|
||||
private readonly array $columns,
|
||||
private string $identifier = '',
|
||||
) {
|
||||
$this->identifier = $identifier ?: md5($label . implode($columns));
|
||||
}
|
||||
|
||||
public function getIdentifier(): string
|
||||
{
|
||||
return $this->identifier;
|
||||
}
|
||||
|
||||
public function getLabel(): string
|
||||
{
|
||||
return $this->label;
|
||||
}
|
||||
|
||||
public function getColumns(): array
|
||||
{
|
||||
return $this->columns;
|
||||
}
|
||||
|
||||
public static function create(array $preset): self
|
||||
{
|
||||
$label = $preset['label'] ?? '';
|
||||
|
||||
if (is_array($preset['columns'] ?? null)) {
|
||||
$columns = $preset['columns'];
|
||||
} else {
|
||||
$columns = GeneralUtility::trimExplode(',', $preset['columns'] ?? '', true);
|
||||
}
|
||||
|
||||
// Presets with empty columns or empty label are ignored
|
||||
if ($columns === [] || $label === '') {
|
||||
throw new \InvalidArgumentException('Invalid download preset.', 1718195273);
|
||||
}
|
||||
|
||||
return new self(
|
||||
$label,
|
||||
$columns,
|
||||
$preset['identifier'] ?? '',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
<?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\RecordList;
|
||||
|
||||
use TYPO3\CMS\Backend\Configuration\TranslationConfigurationProvider;
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
|
||||
use TYPO3\CMS\Core\Schema\Capability\TcaSchemaCapability;
|
||||
use TYPO3\CMS\Core\Schema\TcaSchemaFactory;
|
||||
|
||||
/**
|
||||
* Fetches all records like in the records module but returns them as array in order to allow
|
||||
* downloads (e.g. CSV) in the Controller with prepared data.
|
||||
*
|
||||
* This class acts as a composition-based wrapper for DatabaseRecordList for creating records
|
||||
* ready to be downloaded.
|
||||
*
|
||||
* @internal this class is not part of the TYPO3 Core API due to its nature as being a wrapper for DatabaseRecordList and a very specific implementation.
|
||||
*/
|
||||
class DownloadRecordList
|
||||
{
|
||||
public function __construct(
|
||||
protected DatabaseRecordList $recordList,
|
||||
protected TranslationConfigurationProvider $translationConfigurationProvider,
|
||||
protected TcaSchemaFactory $tcaSchemaFactory
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Add header line with field names.
|
||||
*
|
||||
* @param string[] $columnsToRender
|
||||
* @return array the columns to be used / shown.
|
||||
*/
|
||||
public function getHeaderRow(array $columnsToRender): array
|
||||
{
|
||||
// @todo: array_combine() was used in the initial revision already,
|
||||
// probably to filter out illegal values? Looks odd, but may be due to CSV quirks?
|
||||
return array_combine($columnsToRender, $columnsToRender);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches records including translations (if not hidden) from the database in the specified order given by
|
||||
* DatabaseRecordList and returns the prepared records ready to be rendered.
|
||||
*
|
||||
* @param string $table the TCA table
|
||||
* @param string[] $columnsToRender
|
||||
* @param BackendUserAuthentication $backendUser the current backend user needed to check for permissions
|
||||
* @param bool $rawValues Whether the field values should not be processed
|
||||
* @return array[] an array of rows ready to be output
|
||||
*/
|
||||
public function getRecords(
|
||||
string $table,
|
||||
array $columnsToRender,
|
||||
BackendUserAuthentication $backendUser,
|
||||
bool $hideTranslations = false,
|
||||
bool $rawValues = false
|
||||
): array {
|
||||
// Creating the list of fields to include in the SQL query
|
||||
$selectFields = $this->recordList->getFieldsToSelect($table, $columnsToRender);
|
||||
$queryResult = $this->recordList->getQueryBuilder($table, $selectFields)->executeQuery();
|
||||
$schema = $this->tcaSchemaFactory->get($table);
|
||||
$result = [];
|
||||
$languageField = $schema->isLanguageAware() ? $schema->getCapability(TcaSchemaCapability::Language)->getLanguageField()->getName() : null;
|
||||
// Render items
|
||||
while ($row = $queryResult->fetchAssociative()) {
|
||||
// In offline workspace, look for alternative record
|
||||
BackendUtility::workspaceOL($table, $row, $backendUser->workspace, true);
|
||||
if (!is_array($row)) {
|
||||
continue;
|
||||
}
|
||||
$result[] = $this->prepareRow($table, $row, $columnsToRender, $rawValues);
|
||||
if (!$schema->isLanguageAware()) {
|
||||
continue;
|
||||
}
|
||||
if ($hideTranslations) {
|
||||
continue;
|
||||
}
|
||||
// Guard clause so we can quickly return if a record is localized to "all languages"
|
||||
// It should only be possible to localize a record off default (uid 0)
|
||||
if ((int)$row[$languageField] === -1) {
|
||||
continue;
|
||||
}
|
||||
$translationsRaw = $this->translationConfigurationProvider->translationInfo($table, $row['uid'], 0, $row, $selectFields);
|
||||
foreach ($translationsRaw['translations'] ?? [] as $languageId => $translationRow) {
|
||||
// In offline workspace, look for alternative record
|
||||
BackendUtility::workspaceOL($table, $translationRow, $backendUser->workspace, true);
|
||||
if (is_array($translationRow) && $backendUser->checkLanguageAccess($languageId)) {
|
||||
$result[] = $this->prepareRow($table, $translationRow, $columnsToRender, $rawValues);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a DB row to process the values and maps the values to the columns to render
|
||||
* to have the same output.
|
||||
*
|
||||
* @param string $table Table name
|
||||
* @param array $row Current record
|
||||
* @param string[] $columnsToRender the columns to be displayed / downloaded
|
||||
* @param bool $rawValues Whether the field values should not be processed
|
||||
* @return array the prepared row
|
||||
*/
|
||||
protected function prepareRow(string $table, array $row, array $columnsToRender, bool $rawValues): array
|
||||
{
|
||||
$schema = $this->tcaSchemaFactory->get($table);
|
||||
$labelFieldName = $schema->getCapability(TcaSchemaCapability::Label)->getPrimaryFieldName() ?? '';
|
||||
foreach ($columnsToRender as $columnName) {
|
||||
if (!$rawValues) {
|
||||
if ($columnName === $labelFieldName) {
|
||||
$row[$columnName] = BackendUtility::getRecordTitle($table, $row);
|
||||
} elseif ($columnName !== 'pid') {
|
||||
$row[$columnName] = BackendUtility::getProcessedValueExtra($table, $columnName, $row[$columnName], 0, $row['uid'], false, 0, $row);
|
||||
}
|
||||
}
|
||||
}
|
||||
return array_intersect_key($row, array_flip($columnsToRender));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* 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\RecordList;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
use TYPO3\CMS\Core\DataHandling\TableColumnType;
|
||||
use TYPO3\CMS\Core\Domain\RecordInterface;
|
||||
use TYPO3\CMS\Core\Imaging\IconSize;
|
||||
use TYPO3\CMS\Core\Resource\Filter\FileExtensionFilter;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Displays the page tree for browsing database records.
|
||||
*/
|
||||
#[Autoconfigure(public: true, shared: false)]
|
||||
class ElementBrowserRecordList extends DatabaseRecordList
|
||||
{
|
||||
/**
|
||||
* Table name of the field pointing to this element browser
|
||||
*/
|
||||
protected string $relatingTable = '';
|
||||
|
||||
/**
|
||||
* Field name of the field pointing to this element browser
|
||||
*/
|
||||
protected string $relatingField = '';
|
||||
|
||||
/**
|
||||
* Returns the title (based on $code) of a record (from table $table) with the proper link around (that is for "pages"-records a link to the level of that record...)
|
||||
*/
|
||||
public function linkWrapItems(string $table, int $uid, string $code, RecordInterface $record): string
|
||||
{
|
||||
$row = $record->getRawRecord()->toArray();
|
||||
if (!$code) {
|
||||
$code = '<i>[' . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.no_title')) . ']</i>';
|
||||
} else {
|
||||
$code = BackendUtility::getRecordTitlePrep($code);
|
||||
}
|
||||
$title = BackendUtility::getRecordTitle($table, $row);
|
||||
|
||||
$ATag = '<a href="#" data-close="0" title="' . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_browse_links.xlf:addToList')) . '">';
|
||||
$ATag_alt = '<a href="#" data-close="1" title="' . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang_browse_links.xlf:addToList')) . '">';
|
||||
$ATag_e = '</a>';
|
||||
$out = '<span data-uid="' . htmlspecialchars($row['uid']) . '" data-table="' . htmlspecialchars($table) . '" data-title="' . htmlspecialchars($title) . '">';
|
||||
$out .= $ATag . $this->iconFactory->getIcon('actions-plus', IconSize::SMALL)->render() . $ATag_e . $ATag_alt . $code . $ATag_e;
|
||||
$out .= '</span>';
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if all row listing conditions are fulfilled.
|
||||
*
|
||||
* @param RecordInterface $record Record
|
||||
* @return bool True, if all conditions are fulfilled.
|
||||
*/
|
||||
protected function isRowListingConditionFulfilled(RecordInterface $record): bool
|
||||
{
|
||||
$table = $record->getMainType();
|
||||
$returnValue = true;
|
||||
if (!$this->relatingField) {
|
||||
return true;
|
||||
}
|
||||
if (!$this->relatingTable) {
|
||||
return true;
|
||||
}
|
||||
$schema = $this->tcaSchemaFactory->get($this->relatingTable);
|
||||
$field = $schema->getField($this->relatingField);
|
||||
$tcaFieldConfig = $field->getConfiguration();
|
||||
foreach ($tcaFieldConfig['filter'] ?? [] as $filter) {
|
||||
if (!$filter['userFunc']) {
|
||||
continue;
|
||||
}
|
||||
$parameters = $filter['parameters'] ?? [];
|
||||
$parameters['values'] = [$table . '_' . $record->getUid()];
|
||||
$parameters['tcaFieldConfig'] = $tcaFieldConfig;
|
||||
$valueArray = GeneralUtility::callUserFunction($filter['userFunc'], $parameters, $this);
|
||||
if (empty($valueArray)) {
|
||||
$returnValue = false;
|
||||
}
|
||||
}
|
||||
if ($field->isType(TableColumnType::FILE)) {
|
||||
/** @var FileExtensionFilter $fileExtensionFilter */
|
||||
$fileExtensionFilter = GeneralUtility::makeInstance(FileExtensionFilter::class);
|
||||
$valueArray = $fileExtensionFilter->filter(
|
||||
[$table . '_' . $record->getUid()],
|
||||
(string)($tcaFieldConfig['allowed'] ?? ''),
|
||||
(string)($tcaFieldConfig['disallowed'] ?? ''),
|
||||
);
|
||||
if ($valueArray === []) {
|
||||
$returnValue = false;
|
||||
}
|
||||
}
|
||||
return $returnValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set which pointing field (in the TCEForm) we are currently rendering the element browser for
|
||||
*
|
||||
* @param string $tableName Table name
|
||||
* @param string $fieldName Field name
|
||||
*/
|
||||
public function setRelatingTableAndField(string $tableName, string $fieldName): void
|
||||
{
|
||||
// Check validity of the input data
|
||||
if ($this->tcaSchemaFactory->has($tableName)) {
|
||||
$this->relatingTable = $tableName;
|
||||
if ($this->tcaSchemaFactory->get($tableName)->hasField($fieldName)) {
|
||||
$this->relatingField = $fieldName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?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\RecordList\Event;
|
||||
|
||||
use TYPO3\CMS\Backend\RecordList\DatabaseRecordList;
|
||||
use TYPO3\CMS\Core\Domain\RecordInterface;
|
||||
|
||||
/**
|
||||
* An event to modify the record data for a table in the RecordList.
|
||||
*/
|
||||
final class AfterRecordListRowPreparedEvent
|
||||
{
|
||||
public function __construct(
|
||||
private readonly string $table,
|
||||
private readonly RecordInterface $record,
|
||||
private array $data,
|
||||
private readonly DatabaseRecordList $recordList,
|
||||
private readonly ?string $recTitle,
|
||||
private readonly array|bool $lockInfo,
|
||||
private array $tagAttributes,
|
||||
) {}
|
||||
|
||||
public function getTable(): string
|
||||
{
|
||||
return $this->table;
|
||||
}
|
||||
|
||||
public function getRecord(): RecordInterface
|
||||
{
|
||||
return $this->record;
|
||||
}
|
||||
|
||||
public function getData(): array
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function setData(array $data): void
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
public function getRecTitle(): ?string
|
||||
{
|
||||
return $this->recTitle;
|
||||
}
|
||||
|
||||
public function getLockInfo(): bool|array
|
||||
{
|
||||
return $this->lockInfo;
|
||||
}
|
||||
|
||||
public function getRecordList(): DatabaseRecordList
|
||||
{
|
||||
return $this->recordList;
|
||||
}
|
||||
|
||||
public function getTagAttributes(): array
|
||||
{
|
||||
return $this->tagAttributes;
|
||||
}
|
||||
|
||||
public function setTagAttributes(array $tagAttributes): void
|
||||
{
|
||||
$this->tagAttributes = $tagAttributes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
<?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\RecordList\Event;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
/**
|
||||
* Listeners to this event are able to manipulate the download of records, usually triggered via Content > Record.
|
||||
*/
|
||||
final class BeforeRecordDownloadIsExecutedEvent
|
||||
{
|
||||
/**
|
||||
* @param array $headerRow - Array of downloaded header metadata
|
||||
* @param array $records - Array of the actual data
|
||||
* @param ServerRequestInterface $request - PSR request context (for the actual download request)
|
||||
* @param string $table - Name of the originating database table
|
||||
* @param string $format - Format of the exported data (JSON/CSV)
|
||||
* @param string $filename - Name of the exported file for download
|
||||
* @param int $id - Page uid from where records are fetched
|
||||
* @param array $modTSconfig - Currently applied TS config when exporting
|
||||
* @param array $columnsToRender - Array of selected columns that were fetched
|
||||
* @param bool $hideTranslations - Hide translations?
|
||||
*/
|
||||
public function __construct(
|
||||
private array $headerRow,
|
||||
private array $records,
|
||||
private readonly ServerRequestInterface $request,
|
||||
private readonly string $table,
|
||||
private readonly string $format,
|
||||
private readonly string $filename,
|
||||
private readonly int $id,
|
||||
private readonly array $modTSconfig,
|
||||
private readonly array $columnsToRender,
|
||||
private readonly bool $hideTranslations,
|
||||
) {}
|
||||
|
||||
public function getHeaderRow(): array
|
||||
{
|
||||
return $this->headerRow;
|
||||
}
|
||||
|
||||
public function setHeaderRow(array $headerRow): void
|
||||
{
|
||||
$this->headerRow = $headerRow;
|
||||
}
|
||||
|
||||
public function getRecords(): array
|
||||
{
|
||||
return $this->records;
|
||||
}
|
||||
|
||||
public function setRecords(array $records): void
|
||||
{
|
||||
$this->records = $records;
|
||||
}
|
||||
|
||||
public function getRequest(): ServerRequestInterface
|
||||
{
|
||||
return $this->request;
|
||||
}
|
||||
|
||||
public function getTable(): string
|
||||
{
|
||||
return $this->table;
|
||||
}
|
||||
|
||||
public function getFormat(): string
|
||||
{
|
||||
return $this->format;
|
||||
}
|
||||
|
||||
public function getFilename(): string
|
||||
{
|
||||
return $this->filename;
|
||||
}
|
||||
|
||||
public function getId(): int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getModTSconfig(): array
|
||||
{
|
||||
return $this->modTSconfig;
|
||||
}
|
||||
|
||||
public function getColumnsToRender(): array
|
||||
{
|
||||
return $this->columnsToRender;
|
||||
}
|
||||
|
||||
public function isHideTranslations(): bool
|
||||
{
|
||||
return $this->hideTranslations;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
<?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\RecordList\Event;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Backend\RecordList\DownloadPreset;
|
||||
|
||||
/**
|
||||
* Event to manipulate the available list of download presets.
|
||||
*
|
||||
* Array $presets contains a list of DownloadPreset objects
|
||||
* with their methods: `getIdentifier()`, `getLabel()` and `getColumns()`.
|
||||
*
|
||||
* The event is always coupled to a specific database table.
|
||||
*/
|
||||
final class BeforeRecordDownloadPresetsAreDisplayedEvent
|
||||
{
|
||||
/** @var DownloadPreset[] */
|
||||
private array $presets;
|
||||
|
||||
/**
|
||||
* @param string $table - Name of the originating database table
|
||||
* @param array<string|int, array{columns: string|string[]|null, label: string|null}> $presets - Contains list of sub-arrays with keys "label" (string, name of the preset) and "columns" (string, comma-separated list of columns included in the preset)
|
||||
* @param ServerRequestInterface $request - Request-context of the action that displays the preset
|
||||
* @param int $id - Page ID where the records are stored
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly string $table,
|
||||
array $presets,
|
||||
private readonly ServerRequestInterface $request,
|
||||
private readonly int $id,
|
||||
) {
|
||||
$this->setPresets($presets);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return DownloadPreset[]
|
||||
*/
|
||||
public function getPresets(): array
|
||||
{
|
||||
return $this->presets;
|
||||
}
|
||||
|
||||
public function setPresets(array $presets): void
|
||||
{
|
||||
$this->presets = [];
|
||||
foreach ($presets as $preset) {
|
||||
if (is_array($preset)) {
|
||||
try {
|
||||
$preset = DownloadPreset::create($preset);
|
||||
} catch (\InvalidArgumentException) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ($preset instanceof DownloadPreset) {
|
||||
$this->presets[$preset->getIdentifier()] = $preset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getDatabaseTable(): string
|
||||
{
|
||||
return $this->table;
|
||||
}
|
||||
|
||||
public function getRequest(): ServerRequestInterface
|
||||
{
|
||||
return $this->request;
|
||||
}
|
||||
|
||||
public function getId(): int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
<?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\RecordList\Event;
|
||||
|
||||
use TYPO3\CMS\Backend\RecordList\DatabaseRecordList;
|
||||
|
||||
/**
|
||||
* An event to modify the header columns for a table in the RecordList
|
||||
*/
|
||||
final class ModifyRecordListHeaderColumnsEvent
|
||||
{
|
||||
/**
|
||||
* Additional header attributes for the table header row
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
private array $headerAttributes = [];
|
||||
|
||||
/**
|
||||
* @param array<int> $recordIds
|
||||
*/
|
||||
public function __construct(
|
||||
private array $columns,
|
||||
private readonly string $table,
|
||||
private readonly array $recordIds,
|
||||
private readonly DatabaseRecordList $recordList
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Add a new column or override an existing one. Latter is only possible,
|
||||
* in case $columnName is given. Otherwise, the column will be added with
|
||||
* a numeric index, which is generally not recommended.
|
||||
*
|
||||
* Note: Due to the behaviour of DatabaseRecordList, just adding a column
|
||||
* does not mean that it is also displayed. The internal $fieldArray needs
|
||||
* to be adjusted as well. This method only adds the column to the data array.
|
||||
* Therefore, this method should mainly be used to edit existing columns, e.g.
|
||||
* change their label.
|
||||
*/
|
||||
public function setColumn(string $column, string $columnName = ''): void
|
||||
{
|
||||
if ($columnName !== '') {
|
||||
$this->columns[$columnName] = $column;
|
||||
} else {
|
||||
$this->columns[] = $column;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the column exists
|
||||
*/
|
||||
public function hasColumn(string $columnName): bool
|
||||
{
|
||||
return (bool)($this->columns[$columnName] ?? false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get column by its name
|
||||
*
|
||||
* @return string|null The column or NULL if the column does not exist
|
||||
*/
|
||||
public function getColumn(string $columnName): ?string
|
||||
{
|
||||
return $this->columns[$columnName] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove column by its name
|
||||
*
|
||||
* @return bool Whether the column could be removed - Will therefore
|
||||
* return FALSE if the column to remove does not exist.
|
||||
*/
|
||||
public function removeColumn(string $columnName): bool
|
||||
{
|
||||
if (!isset($this->columns[$columnName])) {
|
||||
return false;
|
||||
}
|
||||
unset($this->columns[$columnName]);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function setColumns(array $columns): void
|
||||
{
|
||||
$this->columns = $columns;
|
||||
}
|
||||
|
||||
public function getColumns(): array
|
||||
{
|
||||
return $this->columns;
|
||||
}
|
||||
|
||||
public function setHeaderAttributes(array $headerAttributes): void
|
||||
{
|
||||
$this->headerAttributes = $headerAttributes;
|
||||
}
|
||||
|
||||
public function getHeaderAttributes(): array
|
||||
{
|
||||
return $this->headerAttributes;
|
||||
}
|
||||
|
||||
public function getTable(): string
|
||||
{
|
||||
return $this->table;
|
||||
}
|
||||
|
||||
public function getRecordIds(): array
|
||||
{
|
||||
return $this->recordIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current DatabaseRecordList instance.
|
||||
*
|
||||
* @todo Might be replaced by a DTO in the future
|
||||
*/
|
||||
public function getRecordList(): DatabaseRecordList
|
||||
{
|
||||
return $this->recordList;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
<?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\RecordList\Event;
|
||||
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use TYPO3\CMS\Backend\RecordList\DatabaseRecordList;
|
||||
use TYPO3\CMS\Backend\Template\Components\ActionGroup;
|
||||
use TYPO3\CMS\Backend\Template\Components\ComponentGroup;
|
||||
use TYPO3\CMS\Backend\Template\Components\ComponentInterface;
|
||||
use TYPO3\CMS\Core\Domain\RecordInterface;
|
||||
|
||||
/**
|
||||
* An event to modify the displayed record actions (e.g.
|
||||
* "edit", "copy", "delete") for a table in the RecordList.
|
||||
*/
|
||||
final readonly class ModifyRecordListRecordActionsEvent
|
||||
{
|
||||
public function __construct(
|
||||
private ComponentGroup $primary,
|
||||
private ComponentGroup $secondary,
|
||||
private RecordInterface $record,
|
||||
private DatabaseRecordList $recordList,
|
||||
private RequestInterface $request,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Add a new action or override an existing one. Latter is only possible,
|
||||
* in case $columnName is given. Otherwise, the column will be added with
|
||||
* a numeric index, which is generally not recommended. It's also possible
|
||||
* to define the position of an action with either the "before" or "after"
|
||||
* argument, while their value must be an existing action.
|
||||
*
|
||||
* Note: In case non or an invalid $group is provided, the new action will
|
||||
* be added to the secondary group.
|
||||
*
|
||||
* @param ?ComponentInterface $action
|
||||
* @param string $actionName
|
||||
* @param ActionGroup $group
|
||||
* @param string $before
|
||||
* @param string $after
|
||||
*/
|
||||
public function setAction(
|
||||
?ComponentInterface $action,
|
||||
string $actionName,
|
||||
ActionGroup $group = ActionGroup::secondary,
|
||||
string $before = '',
|
||||
string $after = '',
|
||||
): void {
|
||||
if ($actionName === '') {
|
||||
throw new \Exception('You must provide a valid action name when adding a new action.', 1761584690);
|
||||
}
|
||||
|
||||
$componentGroup = match ($group) {
|
||||
ActionGroup::primary => $this->primary,
|
||||
ActionGroup::secondary => $this->secondary,
|
||||
};
|
||||
$componentGroup->add($actionName, $action, $before, $after);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the action exists in the given group. In case non or
|
||||
* an invalid $group is provided, both groups will be checked.
|
||||
*/
|
||||
public function hasAction(string $actionName, ?ActionGroup $group = null): bool
|
||||
{
|
||||
return match ($group) {
|
||||
ActionGroup::primary => $this->primary->has($actionName),
|
||||
ActionGroup::secondary => $this->secondary->has($actionName),
|
||||
null => $this->primary->has($actionName) || $this->secondary->has($actionName),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get action by its name. In case the action exists in both groups
|
||||
* and non or an invalid $group is provided, the action from the
|
||||
* "primary" group will be returned.
|
||||
*/
|
||||
public function getAction(string $actionName, ?ActionGroup $group = null): ?ComponentInterface
|
||||
{
|
||||
return match ($group) {
|
||||
ActionGroup::primary => $this->primary->get($actionName),
|
||||
ActionGroup::secondary => $this->secondary->get($actionName),
|
||||
null => $this->primary->get($actionName) ?? $this->secondary->get($actionName),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove action by its name. In case the action exists in both groups
|
||||
* and non or an invalid $group is provided, the action will be removed
|
||||
* from both groups.
|
||||
*/
|
||||
public function removeAction(string $actionName, ?ActionGroup $group = null): void
|
||||
{
|
||||
if ($group === null) {
|
||||
$this->primary->remove($actionName);
|
||||
$this->secondary->remove($actionName);
|
||||
return;
|
||||
}
|
||||
match ($group) {
|
||||
ActionGroup::primary => $this->primary->remove($actionName),
|
||||
ActionGroup::secondary => $this->secondary->remove($actionName),
|
||||
};
|
||||
}
|
||||
|
||||
public function moveActionTo(
|
||||
string $actionName,
|
||||
ActionGroup $group,
|
||||
string $before = '',
|
||||
string $after = '',
|
||||
): void {
|
||||
if (!$this->hasAction($actionName)) {
|
||||
throw new \RuntimeException('The action "' . $actionName . '" does not exist and therefore cannot be moved.', 1761646464);
|
||||
}
|
||||
$action = $this->getAction($actionName);
|
||||
$this->removeAction($actionName);
|
||||
$this->setAction($action, $actionName, $group, $before, $after);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the actions of a specific group
|
||||
*/
|
||||
public function getActionGroup(ActionGroup $group): ComponentGroup
|
||||
{
|
||||
return match ($group) {
|
||||
ActionGroup::primary => $this->primary,
|
||||
ActionGroup::secondary => $this->secondary,
|
||||
};
|
||||
}
|
||||
|
||||
public function getRecord(): RecordInterface
|
||||
{
|
||||
return $this->record;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current DatabaseRecordList instance.
|
||||
*
|
||||
* @todo Might be replaced by a DTO in the future
|
||||
*/
|
||||
public function getRecordList(): DatabaseRecordList
|
||||
{
|
||||
return $this->recordList;
|
||||
}
|
||||
|
||||
public function getRequest(): RequestInterface
|
||||
{
|
||||
return $this->request;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
<?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\RecordList\Event;
|
||||
|
||||
use TYPO3\CMS\Backend\RecordList\DatabaseRecordList;
|
||||
|
||||
/**
|
||||
* An event to modify the multi record selection actions (e.g.
|
||||
* "edit", "copy to clipboard") for a table in the RecordList.
|
||||
*/
|
||||
final class ModifyRecordListTableActionsEvent
|
||||
{
|
||||
/**
|
||||
* The label, which will be displayed in case
|
||||
* no action is available for current the user.
|
||||
*/
|
||||
private string $noActionLabel = '';
|
||||
|
||||
/**
|
||||
* @param array<int> $recordIds
|
||||
*/
|
||||
public function __construct(
|
||||
private array $actions,
|
||||
private readonly string $table,
|
||||
private readonly array $recordIds,
|
||||
private readonly DatabaseRecordList $recordList
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Add a new action or override an existing one. Latter is only possible,
|
||||
* in case $actionName is given. Otherwise, the action will be added with
|
||||
* a numeric index, which is generally not recommended. It's also possible
|
||||
* to define the position of an action with either the "before" or "after"
|
||||
* argument, while their value must be an existing action.
|
||||
*/
|
||||
public function setAction(string $action, string $actionName = '', string $before = '', string $after = ''): void
|
||||
{
|
||||
if ($actionName !== '') {
|
||||
if ($before !== '' && $this->hasAction($before)) {
|
||||
$end = array_splice($this->actions, (int)(array_search($before, array_keys($this->actions), true)));
|
||||
$this->actions = array_merge($this->actions, [$actionName => $action], $end);
|
||||
} elseif ($after !== '' && $this->hasAction($after)) {
|
||||
$end = array_splice($this->actions, (int)(array_search($after, array_keys($this->actions), true)) + 1);
|
||||
$this->actions = array_merge($this->actions, [$actionName => $action], $end);
|
||||
} else {
|
||||
$this->actions[$actionName] = $action;
|
||||
}
|
||||
} else {
|
||||
$this->actions[] = $action;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the action exists
|
||||
*/
|
||||
public function hasAction(string $actionName): bool
|
||||
{
|
||||
return (bool)($this->actions[$actionName] ?? false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get action by its name
|
||||
*
|
||||
* @return string|null The action or NULL if the action does not exist
|
||||
*/
|
||||
public function getAction(string $actionName): ?string
|
||||
{
|
||||
return $this->actions[$actionName] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove action by its name
|
||||
*
|
||||
* @return bool Whether the action could be removed - Will therefore
|
||||
* return FALSE if the action to remove does not exist.
|
||||
*/
|
||||
public function removeAction(string $actionName): bool
|
||||
{
|
||||
if (!isset($this->actions[$actionName])) {
|
||||
return false;
|
||||
}
|
||||
unset($this->actions[$actionName]);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function setActions(array $actions): void
|
||||
{
|
||||
$this->actions = $actions;
|
||||
}
|
||||
|
||||
public function getActions(): array
|
||||
{
|
||||
return $this->actions;
|
||||
}
|
||||
|
||||
public function setNoActionLabel(string $noActionLabel): void
|
||||
{
|
||||
$this->noActionLabel = $noActionLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the label, which will be displayed, in case no
|
||||
* action is available for the current user. Note: If
|
||||
* this returns an empty string, this only means that
|
||||
* no other listener set a label before. TYPO3 will
|
||||
* always fall back to a default if this remains empty.
|
||||
*/
|
||||
public function getNoActionLabel(): string
|
||||
{
|
||||
return $this->noActionLabel;
|
||||
}
|
||||
|
||||
public function getTable(): string
|
||||
{
|
||||
return $this->table;
|
||||
}
|
||||
|
||||
public function getRecordIds(): array
|
||||
{
|
||||
return $this->recordIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current DatabaseRecordList instance.
|
||||
*
|
||||
* @todo Might be replaced by a DTO in the future
|
||||
*/
|
||||
public function getRecordList(): DatabaseRecordList
|
||||
{
|
||||
return $this->recordList;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user