From a7ea733075d68a92e728fc714dc473b3f885cc78 Mon Sep 17 00:00:00 2001 From: Eric Gesemann Date: Wed, 3 Jun 2026 18:11:59 +0200 Subject: [PATCH 1/3] Add support for overriding list view and reader pages with new fields and language adjustments --- contao/dca/tl_content.php | 12 ++++++--- contao/dca/tl_flare_list.php | 26 ++++++++++++++++--- contao/languages/de/tl_content.php | 2 ++ contao/languages/de/tl_flare_list.php | 9 ++++--- contao/languages/en/tl_content.php | 2 ++ contao/languages/en/tl_flare_list.php | 9 ++++--- src/DataContainer/ContentContainer.php | 2 ++ .../Factory/InteractiveContextFactory.php | 9 ++++--- .../Factory/ValidationContextFactory.php | 5 +++- src/Engine/Context/ValidationContext.php | 2 ++ .../Contao/LoadDataContainerListener.php | 17 +++++++----- src/Model/DocumentsListModelTrait.php | 3 ++- 12 files changed, 71 insertions(+), 27 deletions(-) diff --git a/contao/dca/tl_content.php b/contao/dca/tl_content.php index 769d9f27..64ddc686 100644 --- a/contao/dca/tl_content.php +++ b/contao/dca/tl_content.php @@ -50,13 +50,14 @@ 'sql' => "int(10) unsigned NOT NULL default 0", ]; -$dca['fields'][$jumpTo = ContentContainer::FIELD_JUMP_TO] = [ +###> jump to page fields ### +$fieldJumpTo = [ 'inputType' => 'pageTree', 'foreignKey' => 'tl_page.title', 'eval' => [ 'mandatory' => false, 'fieldType' => 'radio', - 'tl_class' => 'w50' + 'tl_class' => 'w50', ], 'sql' => [ 'type' => 'integer', @@ -67,6 +68,10 @@ ], 'relation' => ['type' => 'hasOne', 'load' => 'lazy'], ]; +$dca['fields'][$jumpTo = ContentContainer::FIELD_JUMP_TO] = $fieldJumpTo; +$dca['fields'][$jumpToReader = ContentContainer::FIELD_JUMP_TO_READER] = $fieldJumpTo; +$dca['fields'][$jumpToListView = ContentContainer::FIELD_JUMP_TO_LISTVIEW] = $fieldJumpTo; +###< jump to page fields ### $dca['fields'][$dcMultilingualDisplay = ContentContainer::FIELD_DC_MULTILINGUAL_DISPLAY] = [ 'inputType' => 'select', @@ -86,8 +91,9 @@ $dca['palettes'][ListViewController::TYPE] = '{type_legend},type,headline;' . "{flare_list_legend},$list,$formName,$itemsPerPage,$jumpTo;" + . "{flare_reader_legend},$jumpToReader;" . $commonPaletteEnd; $dca['palettes'][ReaderController::TYPE] = '{type_legend},type,headline;' - . "{flare_list_legend},$list;" + . "{flare_list_legend},$list,$jumpToListView;" . $commonPaletteEnd; diff --git a/contao/dca/tl_flare_list.php b/contao/dca/tl_flare_list.php index 07e927b9..8816c1c3 100644 --- a/contao/dca/tl_flare_list.php +++ b/contao/dca/tl_flare_list.php @@ -200,12 +200,32 @@ ], 'sql' => "varchar(128) NULL default ''", ], + 'jumpToListView' => [ + 'exclude' => true, + 'inputType' => 'pageTree', + 'foreignKey' => 'tl_page.title', + 'eval' => ['fieldType' => 'radio', 'tl_class' => 'clr w50'], + 'sql' => [ + 'type' => 'integer', + 'unsigned' => true, + 'length' => 10, + 'notnull' => false, + 'default' => null, + ], + 'relation' => ['type' => 'hasOne', 'load' => 'lazy'], + ], 'jumpToReader' => [ 'exclude' => true, 'inputType' => 'pageTree', 'foreignKey' => 'tl_page.title', - 'eval' => ['fieldType' => 'radio', 'tl_class' => 'clr'], - 'sql' => "int(10) unsigned NOT NULL default 0", + 'eval' => ['fieldType' => 'radio', 'tl_class' => 'w50'], + 'sql' => [ + 'type' => 'integer', + 'unsigned' => true, + 'length' => 10, + 'notnull' => false, + 'default' => null, + ], 'relation' => ['type' => 'hasOne', 'load' => 'lazy'], ], 'sortSettings' => [ @@ -297,7 +317,7 @@ $dca['palettes'] = [ '__selector__' => ['type', 'whichPtable'], '__prefix__' => '{title_legend},title,type', - '__suffix__' => '{flare_defaults_legend},sortSettings;{flare_reader_legend},jumpToReader;{advanced_legend:hide};{publish_legend},published', + '__suffix__' => '{flare_defaults_legend},sortSettings;{flare_jump_legend},jumpToListView,jumpToReader;{advanced_legend:hide};{publish_legend},published', ]; $dca['palettes']['default'] = Str::mergePalettes($dca['palettes']['__prefix__'], $dca['palettes']['__suffix__']); diff --git a/contao/languages/de/tl_content.php b/contao/languages/de/tl_content.php index de59184d..e769d281 100644 --- a/contao/languages/de/tl_content.php +++ b/contao/languages/de/tl_content.php @@ -8,4 +8,6 @@ $lang[ContentContainer::FIELD_FORM_NAME] = ['Formularname', 'Geben Sie hier den Namen des Filter-Formulars ein.']; $lang[ContentContainer::FIELD_ITEMS_PER_PAGE] = ['Elemente pro Seite', 'Geben Sie hier die Anzahl der Elemente pro Seite ein. 0 um alle anzuzeigen.']; $lang[ContentContainer::FIELD_JUMP_TO] = ['Formular weiterleiten', 'Wählen Sie die "action"-Seite, auf die mit Absenden des Formulars weitergeleitet wird.']; +$lang[ContentContainer::FIELD_JUMP_TO_READER] = ['Detailleser-Seite überschreiben', 'Die Detailseite, auf die Listeneinträge verlinken.']; +$lang[ContentContainer::FIELD_JUMP_TO_LISTVIEW] = ['Listenansicht-Seite überschreiben', 'Die Seite, auf die der Zurück-Link führt.']; $lang[ContentContainer::FIELD_LIST] = ['Liste', 'Bitte wählen Sie die anzuzeigende FLARE-Liste.']; diff --git a/contao/languages/de/tl_flare_list.php b/contao/languages/de/tl_flare_list.php index 78b766b5..0299625f 100644 --- a/contao/languages/de/tl_flare_list.php +++ b/contao/languages/de/tl_flare_list.php @@ -29,10 +29,11 @@ $lang['sortSettings__direction'] = ['Sortierreihenfolge', 'Bitte wählen Sie die Sortierreihenfolge.']; ###< flare_defaults_legend ### -###> flare_reader_legend ### -$lang['flare_reader_legend'] = 'Leser-Einstellungen'; -$lang['jumpToReader'] = ['Detailleserseite', 'Bitte wählen Sie die Seite aus, zu der Besucher weitergeleitet werden, wenn sie auf einen Listeneintrag klicken.']; -###< flare_reader_legend ### +###> flare_jump_legend ### +$lang['flare_jump_legend'] = 'Link-Einstellungen'; +$lang['jumpToListView'] = ['Standard Listenansicht-Seite', 'Seite, die beim Klick auf den Zurück-Button geöffnet wird.']; +$lang['jumpToReader'] = ['Standard Detailleser-Seite', 'Seite, die beim Klick auf einen Listeneintrag geöffnet wird.']; +###< flare_jump_legend ### ###> meta_legend ### $lang['meta_legend'] = 'Meta-Einstellungen'; diff --git a/contao/languages/en/tl_content.php b/contao/languages/en/tl_content.php index c0480bbd..8d241c79 100644 --- a/contao/languages/en/tl_content.php +++ b/contao/languages/en/tl_content.php @@ -8,4 +8,6 @@ $lang[ContentContainer::FIELD_FORM_NAME] = ['Form Name', 'Enter the name of the filter form.']; $lang[ContentContainer::FIELD_ITEMS_PER_PAGE] = ['Items per Page', 'Enter the number of items per page. Set to 0 to show all.']; $lang[ContentContainer::FIELD_JUMP_TO] = ['Form Redirect', 'Choose the "action" page to which the form will redirect upon submission.']; +$lang[ContentContainer::FIELD_JUMP_TO_READER] = ['Override Detail Reader Page', 'The detail page to which list entries link.']; +$lang[ContentContainer::FIELD_JUMP_TO_LISTVIEW] = ['Override Back to List View Page', 'The page to which the back link points.']; $lang[ContentContainer::FIELD_LIST] = ['List', 'Please choose the FLARE list to display.']; diff --git a/contao/languages/en/tl_flare_list.php b/contao/languages/en/tl_flare_list.php index c956e196..f13c3fd7 100644 --- a/contao/languages/en/tl_flare_list.php +++ b/contao/languages/en/tl_flare_list.php @@ -29,10 +29,11 @@ $lang['sortSettings__direction'] = ['Sorting Direction', 'Please choose the sorting direction.']; ###< flare_defaults_legend ### -###> flare_reader_legend ### -$lang['flare_reader_legend'] = 'Reader Settings'; -$lang['jumpToReader'] = ['Detail Reader Page', 'Please choose the page to which visitors are redirected when clicking on a list entry.']; -###< flare_reader_legend ### +###> flare_jump_legend ### +$lang['flare_jump_legend'] = 'Link Settings'; +$lang['jumpToListView'] = ['Default List View Page', 'Page opened when clicking the back button.']; +$lang['jumpToReader'] = ['Default Detail Reader Page', 'Page opened when clicking a list entry.']; +###< flare_jump_legend ### ###> meta_legend ### $lang['meta_legend'] = 'Meta Settings'; diff --git a/src/DataContainer/ContentContainer.php b/src/DataContainer/ContentContainer.php index c7494c85..46e8bd37 100644 --- a/src/DataContainer/ContentContainer.php +++ b/src/DataContainer/ContentContainer.php @@ -12,5 +12,7 @@ class ContentContainer public const FIELD_FORM_NAME = 'flare_formName'; public const FIELD_ITEMS_PER_PAGE = 'flare_itemsPerPage'; public const FIELD_JUMP_TO = 'flare_jumpTo'; + public const FIELD_JUMP_TO_READER = 'flare_jumpToReader'; + public const FIELD_JUMP_TO_LISTVIEW = 'flare_jumpToListView'; public const FIELD_LIST = 'flare_list'; } \ No newline at end of file diff --git a/src/Engine/Context/Factory/InteractiveContextFactory.php b/src/Engine/Context/Factory/InteractiveContextFactory.php index c9dc124f..a4238815 100644 --- a/src/Engine/Context/Factory/InteractiveContextFactory.php +++ b/src/Engine/Context/Factory/InteractiveContextFactory.php @@ -5,6 +5,7 @@ namespace HeimrichHannot\FlareBundle\Engine\Context\Factory; use Contao\ContentModel; +use HeimrichHannot\FlareBundle\DataContainer\ContentContainer; use HeimrichHannot\FlareBundle\Engine\Context\InteractiveContext; use HeimrichHannot\FlareBundle\Model\ListModel; use HeimrichHannot\FlareBundle\Paginator\PaginatorConfig; @@ -22,15 +23,15 @@ public function __construct( public function createFromContent(ContentModel $contentModel, ListModel $listModel): InteractiveContext { - $filterFormName = $contentModel->flare_formName ?: 'fl' . $listModel->id; + $filterFormName = $contentModel->{ContentContainer::FIELD_FORM_NAME} ?: ('fl' . $listModel->id); $paginatorConfig = new PaginatorConfig( - itemsPerPage: (int) ($contentModel->flare_itemsPerPage ?: 0), + itemsPerPage: (int) ($contentModel->{ContentContainer::FIELD_ITEMS_PER_PAGE} ?: 0), ); $sortOrderSequence = $this->sortOrderSequenceFactory->createFromListModel($listModel); - $jumpToReaderPageId = (int) ($listModel->jumpToReader ?: $contentModel->flare_jumpToReader); + $jumpToReaderPageId = (int) ($contentModel->{ContentContainer::FIELD_JUMP_TO_READER} ?: $listModel->jumpToReader); $fieldAutoItem = DcaHelper::tryGetColumnName( $listModel->dc, @@ -42,7 +43,7 @@ public function createFromContent(ContentModel $contentModel, ListModel $listMod paginatorConfig: $paginatorConfig, sortOrderSequence: $sortOrderSequence, contentModelId: (int) $contentModel->id, - formActionPage: (int) $contentModel->flare_jumpTo, + formActionPage: (int) $contentModel->{ContentContainer::FIELD_JUMP_TO}, formName: $filterFormName, jumpToReaderPageId: $jumpToReaderPageId, autoItemField: $fieldAutoItem, diff --git a/src/Engine/Context/Factory/ValidationContextFactory.php b/src/Engine/Context/Factory/ValidationContextFactory.php index 61f07571..b5dda3b4 100644 --- a/src/Engine/Context/Factory/ValidationContextFactory.php +++ b/src/Engine/Context/Factory/ValidationContextFactory.php @@ -5,6 +5,7 @@ namespace HeimrichHannot\FlareBundle\Engine\Context\Factory; use Contao\ContentModel; +use HeimrichHannot\FlareBundle\DataContainer\ContentContainer; use HeimrichHannot\FlareBundle\Engine\Context\ValidationContext; use HeimrichHannot\FlareBundle\Engine\View\InteractiveView; use HeimrichHannot\FlareBundle\Model\ListModel; @@ -20,7 +21,8 @@ public function __construct( public function createFromContent(ContentModel $contentModel, ListModel $listModel): ValidationContext { - $jumpToReaderPageId = (int) ($listModel->jumpToReader ?: $contentModel->flare_jumpToReader); + $jumpToReaderPageId = (int) ($contentModel->{ContentContainer::FIELD_JUMP_TO_READER} ?: $listModel->jumpToReader); + $jumpToListViewPageId = (int) ($contentModel->{ContentContainer::FIELD_JUMP_TO_LISTVIEW} ?: $listModel->jumpToListView); $fieldAutoItem = DcaHelper::tryGetColumnName( $listModel->dc, @@ -30,6 +32,7 @@ public function createFromContent(ContentModel $contentModel, ListModel $listMod $config = new ValidationContext( jumpToReaderPageId: $jumpToReaderPageId, + jumpToListViewPageId: $jumpToListViewPageId, autoItemField: $fieldAutoItem, ); diff --git a/src/Engine/Context/ValidationContext.php b/src/Engine/Context/ValidationContext.php index 8fbd7eb6..22cd6b28 100644 --- a/src/Engine/Context/ValidationContext.php +++ b/src/Engine/Context/ValidationContext.php @@ -26,6 +26,7 @@ public static function getContextType(): string public function __construct( private ?\Closure $entryCache = null, #[Assert\PositiveOrZero] public int $jumpToReaderPageId = 0, + #[Assert\PositiveOrZero] public int $jumpToListViewPageId = 0, #[Assert\NotBlank] private string $autoItemField = 'id', private array $filterValues = [], ) { @@ -68,6 +69,7 @@ public function withFilterValues(array $values): self return new self( entryCache: $this->entryCache, jumpToReaderPageId: $this->jumpToReaderPageId, + jumpToListViewPageId: $this->jumpToListViewPageId, autoItemField: $this->autoItemField, filterValues: $values, ); diff --git a/src/EventListener/Contao/LoadDataContainerListener.php b/src/EventListener/Contao/LoadDataContainerListener.php index ffb2a2bb..32028371 100644 --- a/src/EventListener/Contao/LoadDataContainerListener.php +++ b/src/EventListener/Contao/LoadDataContainerListener.php @@ -28,7 +28,10 @@ public function __construct( */ public function __invoke(string $table): void { - if ($table !== 'tl_flare_filter' && $table !== 'tl_flare_list') { + $filterTable = FilterModel::getTable(); + $listTable = ListModel::getTable(); + + if ($table !== $filterTable && $table !== $listTable) { return; } @@ -41,8 +44,8 @@ public function __invoke(string $table): void } $model = match ($table) { - 'tl_flare_filter' => FilterModel::findByPk($id), - 'tl_flare_list' => ListModel::findByPk($id), + $filterTable => FilterModel::findByPk($id), + $listTable => ListModel::findByPk($id), }; if (!$model || !$model->type) { @@ -50,8 +53,8 @@ public function __invoke(string $table): void } $prefix = match ($table) { - 'tl_flare_filter' => 'filter.', - 'tl_flare_list' => 'list.', + $filterTable => 'filter.', + $listTable => 'list.', }; if (!$callbacks = $this->registry->getNamespace($prefix . $model->type)) { @@ -59,8 +62,8 @@ public function __invoke(string $table): void } $container = match ($table) { - 'tl_flare_filter' => $this->filterContainer, - 'tl_flare_list' => $this->listContainer, + $filterTable => $this->filterContainer, + $listTable => $this->listContainer, }; // @phpstan-ignore function.alreadyNarrowedType diff --git a/src/Model/DocumentsListModelTrait.php b/src/Model/DocumentsListModelTrait.php index fd2c1865..c4f74f9f 100644 --- a/src/Model/DocumentsListModelTrait.php +++ b/src/Model/DocumentsListModelTrait.php @@ -13,7 +13,8 @@ * @property bool $comments_enabled * @property bool $comments_sendNativeEmails * @property string $fieldAutoItem - * @property string $jumpToReader + * @property ?int $jumpToReader + * @property ?int $jumpToListView * @property array $sortSettings * @property string $metaTitleFormat * @property string $metaDescriptionFormat From 4df3a802bc419276a83189b87b0ecaef73337eae Mon Sep 17 00:00:00 2001 From: Eric Gesemann Date: Wed, 3 Jun 2026 18:14:37 +0200 Subject: [PATCH 2/3] Add `flare_reader_legend` to language files --- contao/languages/de/tl_content.php | 1 + contao/languages/en/tl_content.php | 1 + 2 files changed, 2 insertions(+) diff --git a/contao/languages/de/tl_content.php b/contao/languages/de/tl_content.php index e769d281..201bef11 100644 --- a/contao/languages/de/tl_content.php +++ b/contao/languages/de/tl_content.php @@ -5,6 +5,7 @@ $lang = &$GLOBALS['TL_LANG']['tl_content']; $lang['flare_list_legend'] = 'Listen-Einstellungen'; +$lang['flare_reader_legend'] = 'Leser-Einstellungen'; $lang[ContentContainer::FIELD_FORM_NAME] = ['Formularname', 'Geben Sie hier den Namen des Filter-Formulars ein.']; $lang[ContentContainer::FIELD_ITEMS_PER_PAGE] = ['Elemente pro Seite', 'Geben Sie hier die Anzahl der Elemente pro Seite ein. 0 um alle anzuzeigen.']; $lang[ContentContainer::FIELD_JUMP_TO] = ['Formular weiterleiten', 'Wählen Sie die "action"-Seite, auf die mit Absenden des Formulars weitergeleitet wird.']; diff --git a/contao/languages/en/tl_content.php b/contao/languages/en/tl_content.php index 8d241c79..f1e10171 100644 --- a/contao/languages/en/tl_content.php +++ b/contao/languages/en/tl_content.php @@ -5,6 +5,7 @@ $lang = &$GLOBALS['TL_LANG']['tl_content']; $lang['flare_list_legend'] = 'List Settings'; +$lang['flare_reader_legend'] = 'Reader Settings'; $lang[ContentContainer::FIELD_FORM_NAME] = ['Form Name', 'Enter the name of the filter form.']; $lang[ContentContainer::FIELD_ITEMS_PER_PAGE] = ['Items per Page', 'Enter the number of items per page. Set to 0 to show all.']; $lang[ContentContainer::FIELD_JUMP_TO] = ['Form Redirect', 'Choose the "action" page to which the form will redirect upon submission.']; From 44e8b3312d32b9f46678c7e7bf23f91fa124fd12 Mon Sep 17 00:00:00 2001 From: Eric Gesemann Date: Wed, 3 Jun 2026 18:34:45 +0200 Subject: [PATCH 3/3] Refactor LoadDataContainerListener: simplify model resolution and namespace retrieval logic --- .../Contao/LoadDataContainerListener.php | 21 ++++++------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/src/EventListener/Contao/LoadDataContainerListener.php b/src/EventListener/Contao/LoadDataContainerListener.php index 32028371..4ca3b48e 100644 --- a/src/EventListener/Contao/LoadDataContainerListener.php +++ b/src/EventListener/Contao/LoadDataContainerListener.php @@ -43,29 +43,20 @@ public function __invoke(string $table): void return; } - $model = match ($table) { - $filterTable => FilterModel::findByPk($id), - $listTable => ListModel::findByPk($id), + [$modelType, $prefix, $container] = match ($table) { + $filterTable => [FilterModel::findByPk($id)?->type, 'filter.', $this->filterContainer], + $listTable => [ListModel::findByPk($id)?->type, 'list.', $this->listContainer], + default => [null, null, null], }; - if (!$model || !$model->type) { + if (!$modelType || $prefix || !$container) { return; } - $prefix = match ($table) { - $filterTable => 'filter.', - $listTable => 'list.', - }; - - if (!$callbacks = $this->registry->getNamespace($prefix . $model->type)) { + if (!$callbacks = $this->registry->getNamespace($prefix . $modelType)) { return; } - $container = match ($table) { - $filterTable => $this->filterContainer, - $listTable => $this->listContainer, - }; - // @phpstan-ignore function.alreadyNarrowedType if (!\is_subclass_of($container, FlareCallbackContainerInterface::class)) { return;