Add field_redirect_page to site_sections terms and fix root page hierarchy

- Add field_redirect_page to site_sections taxonomy terms, with a custom
  EntityReferenceSelection plugin that filters content_page nodes by section
- Redirect taxonomy term canonical URLs to the configured node (301)
- Fix root page detection in MenuBlock and views to also match nodes whose
  field_parent_page points to a taxonomy_term (not only empty parent)
- Move root taxonomy-term option to the top of the parent-page dropdown
- Add breadcrumb workaround for Gavias notech theme separator rendering
- Add imecc_menu_helper submodule
- Translate config labels and default terms from English to Portuguese

Co-Authored-By: Henrique Bauer <henrique@webcontent.com.br>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-05 07:52:49 -03:00
parent 36c3a2e9c0
commit 276fd028f2
26 changed files with 661 additions and 77 deletions

View File

@@ -16,7 +16,8 @@ use Drupal\structural_pages\ParentEntityHandler\ParentEntityHandlerManagerInterf
/**
* Provides a breadcrumb builder for section_page and content_page content types.
*/
class SectionBreadcrumbBuilder implements BreadcrumbBuilderInterface {
class SectionBreadcrumbBuilder implements BreadcrumbBuilderInterface
{
use StringTranslationTrait;
@@ -60,7 +61,8 @@ class SectionBreadcrumbBuilder implements BreadcrumbBuilderInterface {
/**
* {@inheritdoc}
*/
public function applies(RouteMatchInterface $route_match): bool {
public function applies(RouteMatchInterface $route_match): bool
{
$node = $route_match->getParameter('node');
if ($node instanceof NodeInterface) {
@@ -73,7 +75,8 @@ class SectionBreadcrumbBuilder implements BreadcrumbBuilderInterface {
/**
* {@inheritdoc}
*/
public function build(RouteMatchInterface $route_match): Breadcrumb {
public function build(RouteMatchInterface $route_match): Breadcrumb
{
$breadcrumb = new Breadcrumb();
$breadcrumb->addCacheContexts(['route']);
@@ -94,8 +97,7 @@ class SectionBreadcrumbBuilder implements BreadcrumbBuilderInterface {
if ($context) {
// Use the handler manager to build breadcrumbs for the context entity.
$this->handlerManager->buildBreadcrumbForEntity($breadcrumb, $context['entity']);
}
else {
} else {
// No parent context, check for site section directly.
if ($node->hasField('field_site_section') && !$node->get('field_site_section')->isEmpty()) {
$term_id = $node->get('field_site_section')->target_id;
@@ -108,6 +110,12 @@ class SectionBreadcrumbBuilder implements BreadcrumbBuilderInterface {
$this->addParentBreadcrumbs($breadcrumb, $node);
}
// Gavias notech theme bug bypass: the theme requires an empty element
// before the title to correctly iterate and render '/' separators.
$breadcrumb->addLink(Link::createFromRoute('', '<none>'));
// Append the active page title, as the theme's title resolver fails for nodes.
$breadcrumb->addLink(Link::createFromRoute($node->getTitle(), '<none>'));
return $breadcrumb;
}
@@ -122,7 +130,8 @@ class SectionBreadcrumbBuilder implements BreadcrumbBuilderInterface {
* @return array|null
* An array with 'type' and 'entity' keys, or NULL if no context.
*/
protected function getParentContext(NodeInterface $node): ?array {
protected function getParentContext(NodeInterface $node): ?array
{
if ($node->bundle() !== 'content_page') {
return NULL;
}
@@ -130,9 +139,11 @@ class SectionBreadcrumbBuilder implements BreadcrumbBuilderInterface {
$visited = [];
$current = $node;
while ($current instanceof NodeInterface &&
$current->hasField('field_parent_page') &&
!$current->get('field_parent_page')->isEmpty()) {
while (
$current instanceof NodeInterface &&
$current->hasField('field_parent_page') &&
!$current->get('field_parent_page')->isEmpty()
) {
$parent_field = $current->get('field_parent_page')->first();
if (!$parent_field) {
@@ -180,8 +191,7 @@ class SectionBreadcrumbBuilder implements BreadcrumbBuilderInterface {
// If parent is a node, continue traversing.
if ($parent instanceof NodeInterface) {
$current = $parent;
}
else {
} else {
break;
}
}
@@ -197,7 +207,8 @@ class SectionBreadcrumbBuilder implements BreadcrumbBuilderInterface {
* @param int|string $term_id
* The taxonomy term ID.
*/
protected function addTaxonomyBreadcrumbs(Breadcrumb $breadcrumb, int|string $term_id): void {
protected function addTaxonomyBreadcrumbs(Breadcrumb $breadcrumb, int|string $term_id): void
{
$term_storage = $this->entityTypeManager->getStorage('taxonomy_term');
$ancestors = $term_storage->loadAllParents($term_id);
$ancestors = array_reverse($ancestors);
@@ -219,7 +230,8 @@ class SectionBreadcrumbBuilder implements BreadcrumbBuilderInterface {
* @param \Drupal\node\NodeInterface $node
* The current node.
*/
protected function addParentBreadcrumbs(Breadcrumb $breadcrumb, NodeInterface $node): void {
protected function addParentBreadcrumbs(Breadcrumb $breadcrumb, NodeInterface $node): void
{
$parents = $this->getNodeParents($node);
$parents = array_reverse($parents);
@@ -240,14 +252,17 @@ class SectionBreadcrumbBuilder implements BreadcrumbBuilderInterface {
* @return \Drupal\node\NodeInterface[]
* Array of parent nodes, from closest to farthest.
*/
protected function getNodeParents(NodeInterface $node): array {
protected function getNodeParents(NodeInterface $node): array
{
$parents = [];
$visited = [];
$current = $node;
while ($current instanceof NodeInterface &&
$current->hasField('field_parent_page') &&
!$current->get('field_parent_page')->isEmpty()) {
while (
$current instanceof NodeInterface &&
$current->hasField('field_parent_page') &&
!$current->get('field_parent_page')->isEmpty()
) {
$parent_field = $current->get('field_parent_page')->first();
if (!$parent_field) {