mirror of
https://gitlab.unicamp.br/infimecc_drupal11_modules/structural_pages.git
synced 2026-03-09 18:07:42 -03:00
Altera lógica de Site Section e adiciona redirect em content_page
- Adiciona campo field_redirect_link (link) no bundle content_page; EventSubscriber emite redirect 301 quando o campo está preenchido - field_site_section passa a ser obrigatório - Formulário de content_page: AJAX no site section, select hierárquico de página pai filtrado por seção, validação customizada - StructuralPagesMenuBlock: MAX_DEPTH 10→50, nova lógica de raiz via field_site_section, variável ancestor_url no render array - Template do menu: novas classes BEM/Gva, suporte a is_redirect, usa ancestor_url em vez de chamada Twig direta - CSS do menu reescrito com estilos flyout/sidebar; Select2 adicionado para o campo de página pai no formulário admin - display_submitted desabilitado no tipo content_page Co-Authored-By: Bauer <henrique@webcontent.com.br> Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -212,6 +212,7 @@ function structural_pages_theme(): array {
|
||||
'structural_pages_menu' => [
|
||||
'variables' => [
|
||||
'ancestor' => NULL,
|
||||
'ancestor_url' => '',
|
||||
'tree' => [],
|
||||
'active_trail' => [],
|
||||
'show_ancestor_title' => TRUE,
|
||||
@@ -332,3 +333,285 @@ function _structural_pages_get_term_hierarchy_path(TermInterface $term): string
|
||||
|
||||
return implode('/', $path_parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter() for node_content_page_form.
|
||||
*/
|
||||
function structural_pages_form_node_content_page_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
|
||||
_structural_pages_alter_parent_page_form($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_form_FORM_ID_alter() for node_content_page_edit_form.
|
||||
*/
|
||||
function structural_pages_form_node_content_page_edit_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
|
||||
_structural_pages_alter_parent_page_form($form, $form_state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to alter the content_page forms for parent page filtering.
|
||||
*/
|
||||
function _structural_pages_alter_parent_page_form(&$form, \Drupal\Core\Form\FormStateInterface $form_state) {
|
||||
// Hide the native Drupal menu settings tab, since this module generates
|
||||
// the structural pages menu automatically.
|
||||
if (isset($form['menu'])) {
|
||||
$form['menu']['#access'] = FALSE;
|
||||
}
|
||||
|
||||
if (isset($form['field_site_section']) && isset($form['field_parent_page'])) {
|
||||
|
||||
// 1. Add AJAX behavior to update parent page options when section changes.
|
||||
if (isset($form['field_site_section']['widget'])) {
|
||||
$form['field_site_section']['widget']['#ajax'] = [
|
||||
'callback' => 'structural_pages_parent_page_ajax_callback',
|
||||
'wrapper' => 'parent-page-wrapper',
|
||||
];
|
||||
}
|
||||
|
||||
// Wrap the container where our custom select will go.
|
||||
$form['structural_pages_wrapper'] = [
|
||||
'#type' => 'container',
|
||||
'#attributes' => ['id' => 'parent-page-wrapper'],
|
||||
'#weight' => $form['field_parent_page']['#weight'] ?? 1,
|
||||
'#attached' => [
|
||||
'library' => [
|
||||
'structural_pages/select2',
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
// 3. Retrieve the currently selected site_section ID.
|
||||
$site_section_id = NULL;
|
||||
|
||||
// From AJAX state (user just changed it).
|
||||
$site_section_value = $form_state->getValue('field_site_section');
|
||||
if (!empty($site_section_value[0]['target_id'])) {
|
||||
$site_section_id = $site_section_value[0]['target_id'];
|
||||
}
|
||||
// From initial form load.
|
||||
elseif (isset($form['field_site_section']['widget']['#default_value'])) {
|
||||
$default = $form['field_site_section']['widget']['#default_value'];
|
||||
if (is_array($default) && !empty($default[0])) {
|
||||
$site_section_id = $default[0];
|
||||
}
|
||||
elseif (is_scalar($default)) {
|
||||
$site_section_id = $default;
|
||||
}
|
||||
}
|
||||
|
||||
$current_entity = $form_state->getFormObject()->getEntity();
|
||||
$current_nid = $current_entity ? $current_entity->id() : NULL;
|
||||
|
||||
if ($site_section_id) {
|
||||
$options = _structural_pages_build_parent_tree_options($site_section_id, $current_nid);
|
||||
|
||||
$default_parent = '';
|
||||
if ($current_entity && !$current_entity->isNew() && !$current_entity->get('field_parent_page')->isEmpty()) {
|
||||
$default_parent = $current_entity->get('field_parent_page')->target_type . ':' . $current_entity->get('field_parent_page')->target_id;
|
||||
}
|
||||
|
||||
// Hide the original field.
|
||||
$form['field_parent_page']['#access'] = FALSE;
|
||||
|
||||
// Create a new visual field.
|
||||
$form['structural_pages_wrapper']['custom_parent_page'] = [
|
||||
'#type' => 'select',
|
||||
'#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.'),
|
||||
// Attach a select2/chosen class if available in standard themes
|
||||
'#attributes' => [
|
||||
'class' => ['select2-widget', 'chosen-enable'],
|
||||
'data-placeholder' => t('Search for a parent page...'),
|
||||
],
|
||||
];
|
||||
|
||||
// We must add a custom submit/validate handler to map our select back to field_parent_page.
|
||||
array_unshift($form['#validate'], 'structural_pages_custom_parent_validate');
|
||||
} else {
|
||||
// If no section chosen yet, hide parent page completely so they pick a section first.
|
||||
$form['field_parent_page']['#access'] = FALSE;
|
||||
$form['structural_pages_wrapper']['custom_parent_page'] = [
|
||||
'#type' => 'markup',
|
||||
'#markup' => '<p><em>' . t('Por favor, selecione uma Site Section primeiro.') . '</em></p>',
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validation callback to map the select list back into dynamic_entity_reference.
|
||||
*/
|
||||
function structural_pages_custom_parent_validate(&$form, \Drupal\Core\Form\FormStateInterface $form_state) {
|
||||
$custom_val = $form_state->getValue('custom_parent_page');
|
||||
if (!empty($custom_val)) {
|
||||
list($type, $id) = explode(':', $custom_val);
|
||||
$form_state->setValue('field_parent_page', [
|
||||
['target_id' => $id, 'target_type' => $type]
|
||||
]);
|
||||
} else {
|
||||
$form_state->setValue('field_parent_page', []);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX callback to replace the parent page wrapper.
|
||||
*/
|
||||
function structural_pages_parent_page_ajax_callback(array &$form, \Drupal\Core\Form\FormStateInterface $form_state) {
|
||||
return $form['structural_pages_wrapper'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a hierarchical tree array of content_pages for a given section.
|
||||
*/
|
||||
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')
|
||||
->condition('field_site_section', $site_section_id)
|
||||
->accessCheck(TRUE)
|
||||
->sort('title', 'ASC');
|
||||
$nids = $query->execute();
|
||||
|
||||
if (empty($nids)) {
|
||||
return $options;
|
||||
}
|
||||
|
||||
$nodes = \Drupal::entityTypeManager()->getStorage('node')->loadMultiple($nids);
|
||||
|
||||
$children = [];
|
||||
$roots = [];
|
||||
|
||||
foreach ($nodes as $nid => $node) {
|
||||
if ($nid == $current_node_id) {
|
||||
continue; // Skip self
|
||||
}
|
||||
|
||||
$parent_id = NULL;
|
||||
$parent_type = NULL;
|
||||
if (!$node->get('field_parent_page')->isEmpty()) {
|
||||
$parent_id = $node->get('field_parent_page')->target_id;
|
||||
$parent_type = $node->get('field_parent_page')->target_type;
|
||||
}
|
||||
|
||||
// If the parent is another content_page in our list, it's a child.
|
||||
if ($parent_type === 'node' && isset($nodes[$parent_id])) {
|
||||
$children[$parent_id][] = $nid;
|
||||
} else {
|
||||
// It's a root (parent is a taxonomy term, a section_page, or empty)
|
||||
$roots[] = $nid;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to sort roots alphabetically
|
||||
usort($roots, function($a, $b) use ($nodes) {
|
||||
return strcmp($nodes[$a]->getTitle(), $nodes[$b]->getTitle());
|
||||
});
|
||||
|
||||
$build_options = function($nids, $depth) use (&$build_options, &$options, $nodes, $children) {
|
||||
// We use non-breaking spaces and hyphens for a tree view look
|
||||
$prefix = str_repeat('— ', $depth);
|
||||
foreach ($nids as $nid) {
|
||||
$options['node:' . $nid] = $prefix . $nodes[$nid]->getTitle();
|
||||
if (isset($children[$nid])) {
|
||||
// Sort children alphabetically too
|
||||
$child_nids = $children[$nid];
|
||||
usort($child_nids, function($a, $b) use ($nodes) {
|
||||
return strcmp($nodes[$a]->getTitle(), $nodes[$b]->getTitle());
|
||||
});
|
||||
$build_options($child_nids, $depth + 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$build_options($roots, 0);
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_preprocess_node().
|
||||
*/
|
||||
function structural_pages_preprocess_node(array &$variables): void {
|
||||
// Forcefully remove the "submitted by" author and date information
|
||||
// for content pages, regardless of theme settings.
|
||||
if (isset($variables['node']) && $variables['node']->bundle() === 'content_page') {
|
||||
$variables['display_submitted'] = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements hook_views_post_execute().
|
||||
*/
|
||||
function structural_pages_views_post_execute(\Drupal\views\ViewExecutable $view) {
|
||||
// Fallback for child_pages view: if empty, show sibling pages instead.
|
||||
if ($view->id() === 'child_pages' && empty($view->result)) {
|
||||
$args = $view->args;
|
||||
if (!empty($args[0]) && is_numeric($args[0])) {
|
||||
$node_storage = \Drupal::entityTypeManager()->getStorage('node');
|
||||
$current_node = $node_storage->load($args[0]);
|
||||
|
||||
if ($current_node instanceof \Drupal\node\NodeInterface && $current_node->bundle() === 'content_page') {
|
||||
$query = \Drupal::entityQuery('node')
|
||||
->condition('type', 'content_page')
|
||||
->condition('status', 1)
|
||||
->accessCheck(TRUE)
|
||||
->sort('title', 'ASC');
|
||||
|
||||
$parent_id = NULL;
|
||||
if (!$current_node->get('field_parent_page')->isEmpty()) {
|
||||
$parent_field = $current_node->get('field_parent_page')->first();
|
||||
if (($parent_field->target_type ?? '') === 'node') {
|
||||
$parent_id = $parent_field->target_id;
|
||||
}
|
||||
}
|
||||
|
||||
if ($parent_id) {
|
||||
// Has a node parent. Siblings are nodes with the same parent.
|
||||
$query->condition('field_parent_page.target_id', $parent_id);
|
||||
$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');
|
||||
if (!$current_node->get('field_site_section')->isEmpty()) {
|
||||
$query->condition('field_site_section.target_id', $current_node->get('field_site_section')->target_id);
|
||||
}
|
||||
}
|
||||
|
||||
$nids = $query->execute();
|
||||
|
||||
if (!empty($nids)) {
|
||||
// Load the sibling nodes
|
||||
$siblings = $node_storage->loadMultiple($nids);
|
||||
$index = 0;
|
||||
$view->result = [];
|
||||
|
||||
foreach ($siblings as $nid => $sibling) {
|
||||
$row = new \Drupal\views\ResultRow();
|
||||
$row->_entity = $sibling;
|
||||
$row->nid = $nid;
|
||||
$row->index = $index++;
|
||||
$view->result[] = $row;
|
||||
}
|
||||
|
||||
$view->total_rows = count($view->result);
|
||||
|
||||
// Optionally alter the title to indicate these are siblings
|
||||
$view->setTitle(t('Páginas do mesmo nível'));
|
||||
|
||||
// Remove empty text since we now have results
|
||||
$view->empty = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user