mirror of
https://gitlab.unicamp.br/infimecc_drupal11_modules/structural_pages.git
synced 2026-03-08 01:27:42 -03:00
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:
@@ -0,0 +1,59 @@
|
||||
langcode: pt-br
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.field.taxonomy_term.site_sections.field_redirect_page
|
||||
- field.field.taxonomy_term.site_sections.layout_builder__layout
|
||||
- taxonomy.vocabulary.site_sections
|
||||
module:
|
||||
- path
|
||||
- text
|
||||
id: taxonomy_term.site_sections.default
|
||||
targetEntityType: taxonomy_term
|
||||
bundle: site_sections
|
||||
mode: default
|
||||
content:
|
||||
description:
|
||||
type: text_textfield
|
||||
weight: 0
|
||||
region: content
|
||||
settings:
|
||||
size: 60
|
||||
placeholder: ''
|
||||
third_party_settings: { }
|
||||
field_redirect_page:
|
||||
type: options_select
|
||||
weight: 10
|
||||
region: content
|
||||
settings: { }
|
||||
third_party_settings: { }
|
||||
langcode:
|
||||
type: language_select
|
||||
weight: 2
|
||||
region: content
|
||||
settings:
|
||||
include_locked: true
|
||||
third_party_settings: { }
|
||||
name:
|
||||
type: string_textfield
|
||||
weight: -5
|
||||
region: content
|
||||
settings:
|
||||
size: 60
|
||||
placeholder: ''
|
||||
third_party_settings: { }
|
||||
path:
|
||||
type: path
|
||||
weight: 30
|
||||
region: content
|
||||
settings: { }
|
||||
third_party_settings: { }
|
||||
status:
|
||||
type: boolean_checkbox
|
||||
weight: 100
|
||||
region: content
|
||||
settings:
|
||||
display_label: true
|
||||
third_party_settings: { }
|
||||
hidden:
|
||||
layout_builder__layout: true
|
||||
@@ -0,0 +1,45 @@
|
||||
uuid: f6aa2bd0-3f75-4489-b3f9-43429491b548
|
||||
langcode: pt-br
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.field.taxonomy_term.site_sections.layout_builder__layout
|
||||
- taxonomy.vocabulary.site_sections
|
||||
module:
|
||||
- layout_builder
|
||||
- layout_discovery
|
||||
third_party_settings:
|
||||
layout_builder:
|
||||
enabled: true
|
||||
allow_custom: true
|
||||
sections:
|
||||
-
|
||||
layout_id: layout_onecol
|
||||
layout_settings:
|
||||
label: ''
|
||||
components:
|
||||
0663ed2f-1401-4e75-8a69-971333b49bc2:
|
||||
uuid: 0663ed2f-1401-4e75-8a69-971333b49bc2
|
||||
region: content
|
||||
configuration:
|
||||
id: 'field_block:taxonomy_term:site_sections:description'
|
||||
label_display: '0'
|
||||
context_mapping:
|
||||
entity: layout_builder.entity
|
||||
formatter:
|
||||
type: text_default
|
||||
label: hidden
|
||||
settings: { }
|
||||
third_party_settings: { }
|
||||
weight: 0
|
||||
additional: { }
|
||||
third_party_settings: { }
|
||||
id: taxonomy_term.site_sections.default
|
||||
targetEntityType: taxonomy_term
|
||||
bundle: site_sections
|
||||
mode: default
|
||||
content: { }
|
||||
hidden:
|
||||
description: true
|
||||
langcode: true
|
||||
layout_builder__layout: true
|
||||
@@ -13,7 +13,7 @@ field_name: field_parent_page
|
||||
entity_type: node
|
||||
bundle: content_page
|
||||
label: 'Parent Page'
|
||||
description: 'Select the parent entity. The site section will be inherited automatically based on the parent type.'
|
||||
description: 'Selecione a entidade pai. A seção do site será herdada automaticamente com base no tipo de pai.'
|
||||
required: false
|
||||
translatable: false
|
||||
default_value: { }
|
||||
|
||||
@@ -11,7 +11,7 @@ id: node.content_page.field_site_section
|
||||
field_name: field_site_section
|
||||
entity_type: node
|
||||
bundle: content_page
|
||||
label: 'Site Section'
|
||||
label: 'Seção do Site'
|
||||
description: 'For root pages, select the section. For child pages, this field is filled automatically.'
|
||||
required: true
|
||||
translatable: false
|
||||
|
||||
@@ -11,8 +11,8 @@ id: node.section_page.field_site_section
|
||||
field_name: field_site_section
|
||||
entity_type: node
|
||||
bundle: section_page
|
||||
label: 'Site Section'
|
||||
description: 'Select the site section this page belongs to.'
|
||||
label: 'Seção do Site'
|
||||
description: 'Selecione a seção do site à qual esta página pertence.'
|
||||
required: true
|
||||
translatable: false
|
||||
default_value: { }
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
langcode: pt-br
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.storage.taxonomy_term.field_redirect_page
|
||||
- node.type.content_page
|
||||
- taxonomy.vocabulary.site_sections
|
||||
id: taxonomy_term.site_sections.field_redirect_page
|
||||
field_name: field_redirect_page
|
||||
entity_type: taxonomy_term
|
||||
bundle: site_sections
|
||||
label: 'Página de Redirecionamento'
|
||||
description: ''
|
||||
required: false
|
||||
translatable: true
|
||||
default_value: { }
|
||||
default_value_callback: ''
|
||||
settings:
|
||||
handler: 'default:node_site_section_redirect'
|
||||
handler_settings:
|
||||
target_bundles:
|
||||
content_page: content_page
|
||||
field_type: entity_reference
|
||||
@@ -0,0 +1,21 @@
|
||||
uuid: 726b6570-a8df-4518-8542-28446352fe46
|
||||
langcode: pt-br
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.storage.taxonomy_term.layout_builder__layout
|
||||
- taxonomy.vocabulary.site_sections
|
||||
module:
|
||||
- layout_builder
|
||||
id: taxonomy_term.site_sections.layout_builder__layout
|
||||
field_name: layout_builder__layout
|
||||
entity_type: taxonomy_term
|
||||
bundle: site_sections
|
||||
label: Layout
|
||||
description: ''
|
||||
required: false
|
||||
translatable: false
|
||||
default_value: { }
|
||||
default_value_callback: ''
|
||||
settings: { }
|
||||
field_type: layout_section
|
||||
@@ -0,0 +1,19 @@
|
||||
langcode: pt-br
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- node
|
||||
- taxonomy
|
||||
id: taxonomy_term.field_redirect_page
|
||||
field_name: field_redirect_page
|
||||
entity_type: taxonomy_term
|
||||
type: entity_reference
|
||||
settings:
|
||||
target_type: node
|
||||
module: core
|
||||
locked: false
|
||||
cardinality: 1
|
||||
translatable: true
|
||||
indexes: { }
|
||||
persist_with_no_fields: false
|
||||
custom_storage: false
|
||||
@@ -0,0 +1,19 @@
|
||||
uuid: 99f94609-3b7c-490e-80a0-96036b03e9d3
|
||||
langcode: pt-br
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- layout_builder
|
||||
- taxonomy
|
||||
id: taxonomy_term.layout_builder__layout
|
||||
field_name: layout_builder__layout
|
||||
entity_type: taxonomy_term
|
||||
type: layout_section
|
||||
settings: { }
|
||||
module: layout_builder
|
||||
locked: true
|
||||
cardinality: 1
|
||||
translatable: false
|
||||
indexes: { }
|
||||
persist_with_no_fields: false
|
||||
custom_storage: false
|
||||
@@ -7,7 +7,7 @@ third_party_settings:
|
||||
name: 'Content Page'
|
||||
type: content_page
|
||||
description: 'Pages with hierarchical parent-child structure for content organization.'
|
||||
help: 'For root pages, fill in the Site Section manually. For child pages, select the parent page and the section will be inherited automatically.'
|
||||
help: 'Para páginas raiz, preencha a Seção do Site manualmente. Para páginas filhas, selecione a página pai e a seção será herdada automaticamente.'
|
||||
new_revision: true
|
||||
preview_mode: 1
|
||||
display_submitted: false
|
||||
|
||||
@@ -6,7 +6,7 @@ third_party_settings:
|
||||
enabled: true
|
||||
name: 'Section Page'
|
||||
type: section_page
|
||||
description: 'Pages organized by site section, with URLs and breadcrumbs based on taxonomy.'
|
||||
description: 'Páginas organizadas por seção do site, com URLs e breadcrumbs baseados na taxonomia.'
|
||||
help: ''
|
||||
new_revision: true
|
||||
preview_mode: 1
|
||||
|
||||
@@ -5,7 +5,7 @@ dependencies:
|
||||
- structural_pages
|
||||
- taxonomy
|
||||
id: site_sections_term
|
||||
label: 'Site Sections Term'
|
||||
label: 'Termo de Seções do Site'
|
||||
type: 'canonical_entities:taxonomy_term'
|
||||
pattern: '[term:hierarchy-path]'
|
||||
selection_logic: and
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies: { }
|
||||
name: 'Site Sections'
|
||||
name: 'Seções do Site'
|
||||
vid: site_sections
|
||||
description: 'Main hierarchical structure for site organization.'
|
||||
description: 'Estrutura hierárquica principal para organização do site.'
|
||||
weight: 0
|
||||
|
||||
6
imecc_menu_helper/imecc_menu_helper.info.yml
Normal file
6
imecc_menu_helper/imecc_menu_helper.info.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
name: 'IMECC Menu Helper'
|
||||
type: module
|
||||
description: 'Automatiza a hierarquia de menus baseada na Taxonomia de Setores.'
|
||||
core_version_requirement: ^10
|
||||
package: Custom
|
||||
configure: imecc_menu_helper.settings
|
||||
5
imecc_menu_helper/imecc_menu_helper.links.menu.yml
Normal file
5
imecc_menu_helper/imecc_menu_helper.links.menu.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
imecc_menu_helper.settings_link:
|
||||
title: 'Hierarquia de Menus IMECC'
|
||||
parent: system.admin_config_content
|
||||
description: 'Mapear Setores para Itens de Menu.'
|
||||
route_name: imecc_menu_helper.settings
|
||||
202
imecc_menu_helper/imecc_menu_helper.module
Normal file
202
imecc_menu_helper/imecc_menu_helper.module
Normal file
@@ -0,0 +1,202 @@
|
||||
<?php
|
||||
|
||||
use Drupal\node\NodeInterface;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Menu\MenuTreeParameters;
|
||||
use Drupal\Core\Routing\TrustedRedirectResponse;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
|
||||
|
||||
/**
|
||||
* Implements hook_node_presave().
|
||||
* (Mantido intacto)
|
||||
*/
|
||||
function imecc_menu_helper_node_presave(NodeInterface $node) {
|
||||
if ($node->getType() != 'pagina') return;
|
||||
|
||||
$field_name = 'field_taxinomia_setor';
|
||||
if (!$node->hasField($field_name) || $node->get($field_name)->isEmpty()) return;
|
||||
|
||||
$setor_tid = $node->get($field_name)->target_id;
|
||||
$config = \Drupal::config('imecc_menu_helper.settings');
|
||||
$parent_mapping_string = $config->get('mapping_' . $setor_tid);
|
||||
|
||||
if (empty($parent_mapping_string)) return;
|
||||
|
||||
$current_parent = $node->menu['menu_parent'] ?? 'main:';
|
||||
|
||||
if ($current_parent != 'main:') {
|
||||
if ($current_parent != $parent_mapping_string) return;
|
||||
}
|
||||
|
||||
if (!$node->isNew() && strpos($parent_mapping_string, $node->uuid()) !== false) return;
|
||||
|
||||
$node->menu['enabled'] = 1;
|
||||
$node->menu['title'] = $node->getTitle();
|
||||
$node->menu['menu_parent'] = $parent_mapping_string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_alter().
|
||||
*/
|
||||
function imecc_menu_helper_form_node_form_alter(&$form, FormStateInterface $form_state, $form_id) {
|
||||
if (!in_array($form_id, ['node_pagina_form', 'node_pagina_edit_form'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
// --- LÓGICA DO MENU (AJAX) ---
|
||||
|
||||
// REMOVIDO: O wrapper 'imecc-menu-wrapper' que causava erro.
|
||||
// Agora atacamos direto o ID nativo 'edit-menu'.
|
||||
|
||||
if (isset($form['field_taxinomia_setor'])) {
|
||||
$form['field_taxinomia_setor']['widget']['#ajax'] = [
|
||||
'callback' => '_imecc_menu_helper_update_menu_options',
|
||||
'wrapper' => 'edit-menu', // <--- CORREÇÃO: Volta para o ID nativo
|
||||
'event' => 'change',
|
||||
'progress' => [
|
||||
'type' => 'throbber',
|
||||
'message' => t('Carregando menu...'),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
// --- LÓGICA DO REDIRECIONAMENTO (Barra Lateral) ---
|
||||
|
||||
$node = $form_state->getFormObject()->getEntity();
|
||||
|
||||
$should_open = FALSE;
|
||||
if ($node->hasField('field_redirecionar_para') && !$node->get('field_redirecionar_para')->isEmpty()) {
|
||||
$should_open = TRUE;
|
||||
}
|
||||
|
||||
$form['redirection_settings'] = [
|
||||
'#type' => 'details',
|
||||
'#title' => t('Redirecionamento'),
|
||||
'#group' => 'advanced',
|
||||
'#open' => $should_open,
|
||||
'#weight' => -10,
|
||||
'#attributes' => [
|
||||
'class' => ['node-form-redirection-settings'],
|
||||
],
|
||||
];
|
||||
|
||||
if (isset($form['field_redirecionar_para'])) {
|
||||
$form['field_redirecionar_para']['#group'] = 'redirection_settings';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback AJAX (CORRIGIDO)
|
||||
*/
|
||||
function _imecc_menu_helper_update_menu_options(array &$form, FormStateInterface $form_state) {
|
||||
$values = $form_state->getValue('field_taxinomia_setor');
|
||||
$tid = 0;
|
||||
|
||||
if (is_array($values) && isset($values[0]['target_id'])) {
|
||||
$tid = $values[0]['target_id'];
|
||||
} elseif (is_array($values) && isset($values['target_id'])) {
|
||||
$tid = $values['target_id'];
|
||||
} elseif (is_numeric($values)) {
|
||||
$tid = $values;
|
||||
}
|
||||
|
||||
// CORREÇÃO: Garante que o ID do formulário se mantenha estável para não quebrar o AJAX
|
||||
$form['menu']['#attributes']['id'] = 'edit-menu';
|
||||
|
||||
if (empty($tid)) return $form['menu'];
|
||||
|
||||
$config = \Drupal::config('imecc_menu_helper.settings');
|
||||
$parent_mapping_string = $config->get('mapping_' . $tid);
|
||||
|
||||
if (empty($parent_mapping_string)) return $form['menu'];
|
||||
|
||||
list($menu_name, $root_plugin_id) = explode(':', $parent_mapping_string, 2);
|
||||
|
||||
/** @var \Drupal\Core\Menu\MenuLinkTreeInterface $menu_tree_service */
|
||||
$menu_tree_service = \Drupal::service('menu.link_tree');
|
||||
|
||||
$parameters = new MenuTreeParameters();
|
||||
$parameters->setRoot($root_plugin_id);
|
||||
$parameters->excludeRoot();
|
||||
$parameters->setMaxDepth(NULL);
|
||||
|
||||
$tree = $menu_tree_service->load($menu_name, $parameters);
|
||||
|
||||
$allowed_ids = [];
|
||||
$allowed_ids[$parent_mapping_string] = true;
|
||||
_imecc_menu_helper_extract_ids($tree, $menu_name, $allowed_ids);
|
||||
|
||||
if (isset($form['menu']['link']['menu_parent']['#options'])) {
|
||||
$options = $form['menu']['link']['menu_parent']['#options'];
|
||||
$filtered_options = [];
|
||||
|
||||
foreach ($options as $key => $label) {
|
||||
if (isset($allowed_ids[$key])) {
|
||||
$filtered_options[$key] = $label;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($filtered_options) && isset($options[$parent_mapping_string])) {
|
||||
$filtered_options[$parent_mapping_string] = $options[$parent_mapping_string];
|
||||
}
|
||||
|
||||
$form['menu']['link']['menu_parent']['#options'] = $filtered_options;
|
||||
|
||||
$current_val = $form_state->getValue(['menu', 'link', 'menu_parent']);
|
||||
if (empty($current_val) || !isset($filtered_options[$current_val])) {
|
||||
$form['menu']['link']['menu_parent']['#value'] = $parent_mapping_string;
|
||||
}
|
||||
}
|
||||
|
||||
$form['menu']['#open'] = TRUE;
|
||||
if (isset($form['menu']['enabled'])) {
|
||||
$form['menu']['enabled']['#value'] = 1;
|
||||
}
|
||||
|
||||
// CORREÇÃO: Retorna o array direto, sem envelopar em arrays novos
|
||||
return $form['menu'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Função auxiliar recursiva (Mantida intacta)
|
||||
*/
|
||||
function _imecc_menu_helper_extract_ids(array $tree, $menu_name, array &$allowed_ids) {
|
||||
foreach ($tree as $element) {
|
||||
$plugin_id = $element->link->getPluginId();
|
||||
$key = $menu_name . ':' . $plugin_id;
|
||||
$allowed_ids[$key] = true;
|
||||
|
||||
if ($element->subtree) {
|
||||
_imecc_menu_helper_extract_ids($element->subtree, $menu_name, $allowed_ids);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_node_view().
|
||||
* (Mantido intacto)
|
||||
*/
|
||||
function imecc_menu_helper_node_view(array &$build, NodeInterface $node, EntityViewDisplayInterface $display, $view_mode) {
|
||||
if ($view_mode !== 'full') return;
|
||||
|
||||
$route_name = \Drupal::routeMatch()->getRouteName();
|
||||
if ($route_name != 'entity.node.canonical') return;
|
||||
|
||||
$field_redirect = 'field_redirecionar_para';
|
||||
|
||||
if ($node->hasField($field_redirect) && !$node->get($field_redirect)->isEmpty()) {
|
||||
|
||||
$url_object = $node->get($field_redirect)->first()->getUrl();
|
||||
$destination = $url_object->toString();
|
||||
|
||||
$current_url = Url::fromRoute('<current>')->toString();
|
||||
|
||||
if ($destination == $current_url) return;
|
||||
|
||||
$response = new TrustedRedirectResponse($destination, 302);
|
||||
$response->addCacheableDependency($node);
|
||||
$response->send();
|
||||
exit;
|
||||
}
|
||||
}
|
||||
3
imecc_menu_helper/imecc_menu_helper.permissions.yml
Normal file
3
imecc_menu_helper/imecc_menu_helper.permissions.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
administer imecc menu helper:
|
||||
title: 'Administrar configuração de menus do IMECC'
|
||||
description: 'Permite configurar o relacionamento entre Setores e Páginas Pai.'
|
||||
7
imecc_menu_helper/imecc_menu_helper.routing.yml
Normal file
7
imecc_menu_helper/imecc_menu_helper.routing.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
imecc_menu_helper.settings:
|
||||
path: '/admin/config/imecc/menu-helper'
|
||||
defaults:
|
||||
_form: '\Drupal\imecc_menu_helper\Form\MenuHelperSettingsForm'
|
||||
_title: 'Configuração de Hierarquia IMECC'
|
||||
requirements:
|
||||
_permission: 'administer imecc menu helper'
|
||||
79
imecc_menu_helper/src/Form/MenuHelperSettingsForm.php
Normal file
79
imecc_menu_helper/src/Form/MenuHelperSettingsForm.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\imecc_menu_helper\Form;
|
||||
|
||||
use Drupal\Core\Form\ConfigFormBase;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Menu\MenuParentFormSelectorInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
|
||||
class MenuHelperSettingsForm extends ConfigFormBase {
|
||||
|
||||
protected $menuParentSelector;
|
||||
|
||||
public function __construct(MenuParentFormSelectorInterface $menu_parent_selector) {
|
||||
$this->menuParentSelector = $menu_parent_selector;
|
||||
}
|
||||
|
||||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('menu.parent_form_selector')
|
||||
);
|
||||
}
|
||||
|
||||
protected function getEditableConfigNames() {
|
||||
return ['imecc_menu_helper.settings'];
|
||||
}
|
||||
|
||||
public function getFormId() {
|
||||
return 'imecc_menu_helper_settings_form';
|
||||
}
|
||||
|
||||
public function buildForm(array $form, FormStateInterface $form_state) {
|
||||
$config = $this->config('imecc_menu_helper.settings');
|
||||
|
||||
$form['description'] = [
|
||||
'#markup' => '<p>Mapeie cada <strong>Setor</strong> (Taxonomia) a um <strong>Item de Menu Pai</strong>. Quando uma página for criada com este setor, ela será automaticamente movida para dentro deste pai.</p>',
|
||||
];
|
||||
|
||||
// Carrega todos os termos do vocabulário 'setores'.
|
||||
$terms = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadTree('setores');
|
||||
|
||||
// Opções de menu (apenas do menu principal).
|
||||
$menu_options = $this->menuParentSelector->getParentSelectOptions('main:', NULL);
|
||||
|
||||
$form['mappings'] = [
|
||||
'#type' => 'details',
|
||||
'#title' => $this->t('Mapeamento Setor -> Menu Pai'),
|
||||
'#open' => TRUE,
|
||||
'#tree' => TRUE,
|
||||
];
|
||||
|
||||
foreach ($terms as $term) {
|
||||
$default_value = $config->get('mapping_' . $term->tid);
|
||||
|
||||
$form['mappings'][$term->tid] = [
|
||||
'#type' => 'select',
|
||||
'#title' => $term->name . ' (' . $term->tid . ')',
|
||||
'#options' => $menu_options,
|
||||
'#default_value' => $default_value,
|
||||
'#empty_option' => '- Sem automação -',
|
||||
'#description' => 'Selecione a página pai para o setor ' . $term->name,
|
||||
];
|
||||
}
|
||||
|
||||
return parent::buildForm($form, $form_state);
|
||||
}
|
||||
|
||||
public function submitForm(array &$form, FormStateInterface $form_state) {
|
||||
$config = $this->config('imecc_menu_helper.settings');
|
||||
$mappings = $form_state->getValue('mappings');
|
||||
|
||||
foreach ($mappings as $tid => $menu_parent) {
|
||||
$config->set('mapping_' . $tid, $menu_parent);
|
||||
}
|
||||
|
||||
$config->save();
|
||||
parent::submitForm($form, $form_state);
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -73,6 +73,26 @@ class StructuralPagesRedirectSubscriber implements EventSubscriberInterface
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif ($route_name === 'entity.taxonomy_term.canonical') {
|
||||
$term = $this->routeMatch->getParameter('taxonomy_term');
|
||||
if ($term instanceof \Drupal\taxonomy\TermInterface && $term->bundle() === 'site_sections') {
|
||||
if ($term->hasField('field_redirect_page') && !$term->get('field_redirect_page')->isEmpty()) {
|
||||
try {
|
||||
$target_node = $term->get('field_redirect_page')->entity;
|
||||
if ($target_node && $target_node->access('view')) {
|
||||
$url = $target_node->toUrl()->toString();
|
||||
|
||||
// Cacheable redirect response.
|
||||
$response = new TrustedRedirectResponse($url, 301);
|
||||
$response->addCacheableDependency($term);
|
||||
$response->addCacheableDependency($target_node);
|
||||
$event->setResponse($response);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// If invalid, proceed normally.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,8 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
/**
|
||||
* Configuration form for Structural Pages module.
|
||||
*/
|
||||
class StructuralPagesSettingsForm extends ConfigFormBase {
|
||||
class StructuralPagesSettingsForm extends ConfigFormBase
|
||||
{
|
||||
|
||||
/**
|
||||
* The entity type manager.
|
||||
@@ -69,7 +70,8 @@ class StructuralPagesSettingsForm extends ConfigFormBase {
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function create(ContainerInterface $container): static {
|
||||
public static function create(ContainerInterface $container): static
|
||||
{
|
||||
return new static(
|
||||
$container->get('config.factory'),
|
||||
$container->get('config.typed'),
|
||||
@@ -82,21 +84,24 @@ class StructuralPagesSettingsForm extends ConfigFormBase {
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getFormId(): string {
|
||||
public function getFormId(): string
|
||||
{
|
||||
return 'structural_pages_settings_form';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getEditableConfigNames(): array {
|
||||
protected function getEditableConfigNames(): array
|
||||
{
|
||||
return ['structural_pages.settings'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildForm(array $form, FormStateInterface $form_state): array {
|
||||
public function buildForm(array $form, FormStateInterface $form_state): array
|
||||
{
|
||||
$config = $this->config('structural_pages.settings');
|
||||
$allowed_targets = $config->get('allowed_parent_targets') ?? [];
|
||||
|
||||
@@ -116,8 +121,8 @@ class StructuralPagesSettingsForm extends ConfigFormBase {
|
||||
|
||||
$form['site_section_vocabulary'] = [
|
||||
'#type' => 'select',
|
||||
'#title' => $this->t('Site section vocabulary'),
|
||||
'#description' => $this->t('Select the taxonomy vocabulary used for site sections. Content pages and section pages will reference terms from this vocabulary.'),
|
||||
'#title' => $this->t('Vocabulário de Seções do Site'),
|
||||
'#description' => $this->t('Selecione o vocabulário de taxonomia usado para as seções do site. As páginas de conteúdo e páginas de seção farão referência aos termos deste vocabulário.'),
|
||||
'#options' => $vocabulary_options,
|
||||
'#default_value' => $config->get('site_section_vocabulary') ?? 'site_sections',
|
||||
'#required' => TRUE,
|
||||
@@ -194,7 +199,8 @@ class StructuralPagesSettingsForm extends ConfigFormBase {
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function submitForm(array &$form, FormStateInterface $form_state): void {
|
||||
public function submitForm(array &$form, FormStateInterface $form_state): void
|
||||
{
|
||||
$vocabulary = $form_state->getValue('site_section_vocabulary');
|
||||
$values = $form_state->getValue('allowed_parent_targets') ?? [];
|
||||
$supportedEntityTypes = $this->handlerManager->getSupportedEntityTypes();
|
||||
@@ -258,7 +264,8 @@ class StructuralPagesSettingsForm extends ConfigFormBase {
|
||||
* @param array $targets
|
||||
* The allowed targets.
|
||||
*/
|
||||
protected function updateFieldConfiguration(array $targets): void {
|
||||
protected function updateFieldConfiguration(array $targets): void
|
||||
{
|
||||
$field_config = $this->entityTypeManager
|
||||
->getStorage('field_config')
|
||||
->load('node.content_page.field_parent_page');
|
||||
@@ -300,8 +307,7 @@ class StructuralPagesSettingsForm extends ConfigFormBase {
|
||||
// Handle wildcard bundles.
|
||||
if ($bundle === '*') {
|
||||
$entity_type_settings[$entity_type]['handler_settings']['target_bundles'] = NULL;
|
||||
}
|
||||
elseif ($entity_type_settings[$entity_type]['handler_settings']['target_bundles'] !== NULL) {
|
||||
} elseif ($entity_type_settings[$entity_type]['handler_settings']['target_bundles'] !== NULL) {
|
||||
$entity_type_settings[$entity_type]['handler_settings']['target_bundles'][$bundle] = $bundle;
|
||||
}
|
||||
}
|
||||
@@ -323,7 +329,8 @@ class StructuralPagesSettingsForm extends ConfigFormBase {
|
||||
* @param string $vocabulary
|
||||
* The vocabulary machine name to set as target bundle.
|
||||
*/
|
||||
protected function updateSiteSectionFieldConfiguration(string $vocabulary): void {
|
||||
protected function updateSiteSectionFieldConfiguration(string $vocabulary): void
|
||||
{
|
||||
$field_names = [
|
||||
'node.section_page.field_site_section',
|
||||
'node.content_page.field_site_section',
|
||||
|
||||
@@ -388,8 +388,11 @@ class StructuralPagesMenuBlock extends BlockBase implements ContainerFactoryPlug
|
||||
->sort('title', 'ASC');
|
||||
|
||||
if ($parent_type === 'taxonomy_term') {
|
||||
// Root pages of a section have no parent page, but belong to the section.
|
||||
$query->notExists('field_parent_page');
|
||||
// Root pages could have an empty parent page OR explicitly point to the taxonomy term.
|
||||
$or_group = $query->orConditionGroup()
|
||||
->notExists('field_parent_page')
|
||||
->condition('field_parent_page.target_type', 'taxonomy_term');
|
||||
$query->condition($or_group);
|
||||
$query->condition('field_site_section.target_id', $parent_id);
|
||||
} else {
|
||||
// Child pages belong to a specific parent entity (node).
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace Drupal\structural_pages\Plugin\EntityReferenceSelection;
|
||||
|
||||
use Drupal\node\Plugin\EntityReferenceSelection\NodeSelection;
|
||||
|
||||
/**
|
||||
* Filter Content Pages by the currently edited Site Section term.
|
||||
*
|
||||
* @EntityReferenceSelection(
|
||||
* id = "default:node_site_section_redirect",
|
||||
* label = @Translation("Site Section Redirect Pages"),
|
||||
* entity_types = {"node"},
|
||||
* group = "default",
|
||||
* weight = 10
|
||||
* )
|
||||
*/
|
||||
class SiteSectionRedirectSelection extends NodeSelection
|
||||
{
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS')
|
||||
{
|
||||
$query = parent::buildEntityQuery($match, $match_operator);
|
||||
|
||||
// Get the taxonomy term being edited.
|
||||
$term = $this->configuration['entity'] ?? NULL;
|
||||
if ($term && $term->getEntityTypeId() === 'taxonomy_term' && $term->bundle() === 'site_sections') {
|
||||
if ($term->id()) {
|
||||
$query->condition('field_site_section', $term->id());
|
||||
} else {
|
||||
// If it's a newly created term, there are no pages for it yet to redirect to.
|
||||
$query->condition('nid', 0);
|
||||
}
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -39,38 +39,38 @@ function _structural_pages_create_default_terms(): void {
|
||||
|
||||
// Structure: name => children.
|
||||
$terms_structure = [
|
||||
'News' => [],
|
||||
'Events' => [],
|
||||
'People' => [],
|
||||
'Institutional' => [
|
||||
'About',
|
||||
'Communication',
|
||||
'Information and Services',
|
||||
'Team',
|
||||
'Management',
|
||||
'Inclusion and Belonging',
|
||||
'Notícias' => [],
|
||||
'Eventos' => [],
|
||||
'Pessoas' => [],
|
||||
'Institucional' => [
|
||||
'Sobre',
|
||||
'Comunicação',
|
||||
'Informações e Serviços',
|
||||
'Equipe',
|
||||
'Gestão',
|
||||
'Inclusão e Pertencimento',
|
||||
],
|
||||
'Undergraduate' => [
|
||||
'Statistics',
|
||||
'Mathematics',
|
||||
'Applied Mathematics',
|
||||
'Mathematics Teaching',
|
||||
'Graduação' => [
|
||||
'Estatística',
|
||||
'Matemática',
|
||||
'Matemática Aplicada',
|
||||
'Licenciatura em Matemática',
|
||||
],
|
||||
'Graduate' => [
|
||||
'Statistics Program',
|
||||
'Mathematics Program',
|
||||
'Applied Mathematics Program',
|
||||
'Pós-Graduação' => [
|
||||
'Programa de Estatística',
|
||||
'Programa de Matemática',
|
||||
'Programa de Matemática Aplicada',
|
||||
],
|
||||
'Research' => [],
|
||||
'Extension' => [],
|
||||
'Administration' => [],
|
||||
'Departments' => [
|
||||
'Statistics Department',
|
||||
'Mathematics Department',
|
||||
'Applied Mathematics Department',
|
||||
'Pesquisa' => [],
|
||||
'Extensão' => [],
|
||||
'Administração' => [],
|
||||
'Departamentos' => [
|
||||
'Departamento de Estatística',
|
||||
'Departamento de Matemática',
|
||||
'Departamento de Matemática Aplicada',
|
||||
],
|
||||
'Library' => [],
|
||||
'IT Services' => [],
|
||||
'Biblioteca' => [],
|
||||
'Informática' => [],
|
||||
];
|
||||
|
||||
$created_terms = [];
|
||||
|
||||
@@ -410,6 +410,17 @@ function _structural_pages_alter_parent_page_form(&$form, \Drupal\Core\Form\Form
|
||||
$default_parent = $current_entity->get('field_parent_page')->target_type . ':' . $current_entity->get('field_parent_page')->target_id;
|
||||
}
|
||||
|
||||
// Add the Root (Taxonomy term) as the default option at the top of the tree.
|
||||
$term = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->load($site_section_id);
|
||||
$term_label = $term ? $term->label() : t('Taxonomia');
|
||||
$root_key = 'taxonomy_term:' . $site_section_id;
|
||||
|
||||
$options = [$root_key => '< ' . t('Raiz (@term)', ['@term' => $term_label]) . ' >'] + $options;
|
||||
|
||||
if (empty($default_parent)) {
|
||||
$default_parent = $root_key;
|
||||
}
|
||||
|
||||
// Hide the original field.
|
||||
$form['field_parent_page']['#access'] = FALSE;
|
||||
|
||||
@@ -419,8 +430,8 @@ function _structural_pages_alter_parent_page_form(&$form, \Drupal\Core\Form\Form
|
||||
'#title' => t('Parent Page'),
|
||||
'#options' => $options,
|
||||
'#default_value' => $default_parent,
|
||||
'#empty_option' => t('- Raiz da Seção -'),
|
||||
'#description' => t('Select the parent page within this site section.'),
|
||||
'#empty_option' => t('- Selecione a Página Pai -'),
|
||||
'#description' => t('Select the parent page within this site section. Select Root if this is a top-level page.'),
|
||||
// Attach a select2/chosen class if available in standard themes
|
||||
'#attributes' => [
|
||||
'class' => ['select2-widget', 'chosen-enable'],
|
||||
@@ -468,12 +479,6 @@ function structural_pages_parent_page_ajax_callback(array &$form, \Drupal\Core\F
|
||||
*/
|
||||
function _structural_pages_build_parent_tree_options($site_section_id, $current_node_id = NULL) {
|
||||
$options = [];
|
||||
|
||||
// Add the Section's Taxonomy Term as a Root option
|
||||
$term = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->load($site_section_id);
|
||||
if ($term) {
|
||||
$options['taxonomy_term:' . $site_section_id] = '< Raiz (' . $term->getName() . ') >';
|
||||
}
|
||||
|
||||
$query = \Drupal::entityQuery('node')
|
||||
->condition('type', 'content_page')
|
||||
@@ -581,7 +586,11 @@ function structural_pages_views_post_execute(\Drupal\views\ViewExecutable $view)
|
||||
$query->condition('field_parent_page.target_type', 'node');
|
||||
} else {
|
||||
// Root page. Siblings are other root pages in the same site section.
|
||||
$query->notExists('field_parent_page');
|
||||
$orGroup = $query->orConditionGroup()
|
||||
->notExists('field_parent_page')
|
||||
->condition('field_parent_page.target_type', 'taxonomy_term');
|
||||
$query->condition($orGroup);
|
||||
|
||||
if (!$current_node->get('field_site_section')->isEmpty()) {
|
||||
$query->condition('field_site_section.target_id', $current_node->get('field_site_section')->target_id);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user