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 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>
627 lines
20 KiB
Plaintext
627 lines
20 KiB
Plaintext
<?php
|
|
|
|
/**
|
|
* @file
|
|
* Primary module hooks for Structural Pages module.
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
use Drupal\Core\Entity\EntityInterface;
|
|
use Drupal\node\NodeInterface;
|
|
use Drupal\taxonomy\TermInterface;
|
|
use Drupal\user\UserInterface;
|
|
|
|
/**
|
|
* Implements hook_entity_presave().
|
|
*
|
|
* Inherits field_site_section from parent to content_page.
|
|
* Validates circular reference in field_parent_page.
|
|
*/
|
|
function structural_pages_entity_presave(EntityInterface $entity): void {
|
|
if (!$entity instanceof NodeInterface || $entity->bundle() !== 'content_page') {
|
|
return;
|
|
}
|
|
|
|
// Check if has parent page.
|
|
if (!$entity->hasField('field_parent_page') || $entity->get('field_parent_page')->isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
$parent_field = $entity->get('field_parent_page')->first();
|
|
if (!$parent_field) {
|
|
return;
|
|
}
|
|
|
|
$parent_entity_type = $parent_field->target_type ?? NULL;
|
|
$parent_id = $parent_field->target_id ?? NULL;
|
|
|
|
if (!$parent_entity_type || !$parent_id) {
|
|
return;
|
|
}
|
|
|
|
// Circular reference validation (only for node parents).
|
|
if ($parent_entity_type === 'node' && !$entity->isNew() && $parent_id) {
|
|
if (_structural_pages_creates_circular_reference($entity->id(), $parent_id)) {
|
|
\Drupal::messenger()->addError(t('Circular reference detected. A page cannot be a parent of itself or its ancestors.'));
|
|
// Remove invalid reference.
|
|
$entity->set('field_parent_page', NULL);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Handle site section based on parent type.
|
|
// Some entity types (like user and group) act as context containers themselves.
|
|
/** @var \Drupal\structural_pages\ParentEntityHandler\ParentEntityHandlerManagerInterface $handler_manager */
|
|
$handler_manager = \Drupal::service('plugin.manager.parent_entity_handler');
|
|
if ($handler_manager->clearsSiteSection($parent_entity_type)) {
|
|
// Clear field_site_section as the context is the parent entity, not a site section.
|
|
$entity->set('field_site_section', NULL);
|
|
return;
|
|
}
|
|
|
|
// Inherit field_site_section based on parent type (node or taxonomy_term).
|
|
$site_section_id = _structural_pages_get_section_from_parent($parent_entity_type, $parent_id);
|
|
if ($site_section_id) {
|
|
$entity->set('field_site_section', $site_section_id);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the configured site section vocabulary machine name.
|
|
*
|
|
* @return string
|
|
* The vocabulary ID (e.g., 'site_sections').
|
|
*/
|
|
function _structural_pages_get_vocabulary(): string {
|
|
return \Drupal::config('structural_pages.settings')
|
|
->get('site_section_vocabulary') ?? 'site_sections';
|
|
}
|
|
|
|
/**
|
|
* Gets the site section ID from a parent entity.
|
|
*
|
|
* @param string $parent_entity_type
|
|
* The parent entity type (node or taxonomy_term).
|
|
* @param int|string $parent_id
|
|
* The parent entity ID.
|
|
*
|
|
* @return int|string|null
|
|
* The site section term ID, or NULL if not found.
|
|
*/
|
|
function _structural_pages_get_section_from_parent(string $parent_entity_type, int|string $parent_id): int|string|null {
|
|
$entity_type_manager = \Drupal::entityTypeManager();
|
|
|
|
if ($parent_entity_type === 'taxonomy_term') {
|
|
// If parent is a taxonomy term, verify it's from the configured vocabulary.
|
|
$term = $entity_type_manager->getStorage('taxonomy_term')->load($parent_id);
|
|
if ($term instanceof TermInterface && $term->bundle() === _structural_pages_get_vocabulary()) {
|
|
return $term->id();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
if ($parent_entity_type === 'node') {
|
|
$parent_node = $entity_type_manager->getStorage('node')->load($parent_id);
|
|
if (!$parent_node instanceof NodeInterface) {
|
|
return NULL;
|
|
}
|
|
|
|
// If parent has field_site_section, use it.
|
|
if ($parent_node->hasField('field_site_section') && !$parent_node->get('field_site_section')->isEmpty()) {
|
|
return $parent_node->get('field_site_section')->target_id;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Gets the parent context information for a content_page.
|
|
*
|
|
* @param \Drupal\node\NodeInterface $node
|
|
* The content_page node.
|
|
*
|
|
* @return array|null
|
|
* An array with 'type' and 'entity' keys, or NULL if no parent.
|
|
*/
|
|
function _structural_pages_get_parent_context(NodeInterface $node): ?array {
|
|
if (!$node->hasField('field_parent_page') || $node->get('field_parent_page')->isEmpty()) {
|
|
return NULL;
|
|
}
|
|
|
|
$parent_field = $node->get('field_parent_page')->first();
|
|
if (!$parent_field) {
|
|
return NULL;
|
|
}
|
|
|
|
$parent_entity_type = $parent_field->target_type ?? NULL;
|
|
$parent_id = $parent_field->target_id ?? NULL;
|
|
|
|
if (!$parent_entity_type || !$parent_id) {
|
|
return NULL;
|
|
}
|
|
|
|
$parent = \Drupal::entityTypeManager()
|
|
->getStorage($parent_entity_type)
|
|
->load($parent_id);
|
|
|
|
if (!$parent) {
|
|
return NULL;
|
|
}
|
|
|
|
return [
|
|
'type' => $parent_entity_type,
|
|
'entity' => $parent,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Checks if setting parent_id as parent of node_id would create circular reference.
|
|
*
|
|
* @param int|string $node_id
|
|
* The ID of the node being edited.
|
|
* @param int|string $parent_id
|
|
* The ID of the potential parent.
|
|
*
|
|
* @return bool
|
|
* TRUE if it would create circular reference, FALSE otherwise.
|
|
*/
|
|
function _structural_pages_creates_circular_reference(int|string $node_id, int|string $parent_id): bool {
|
|
$visited = [];
|
|
$current_id = $parent_id;
|
|
$node_storage = \Drupal::entityTypeManager()->getStorage('node');
|
|
|
|
while ($current_id) {
|
|
// If we find the original node in the parent chain, it's circular.
|
|
if ($current_id == $node_id) {
|
|
return TRUE;
|
|
}
|
|
|
|
// Avoid infinite loops in case of corrupted data.
|
|
if (isset($visited[$current_id])) {
|
|
return TRUE;
|
|
}
|
|
$visited[$current_id] = TRUE;
|
|
|
|
// Load the current node and get its parent.
|
|
$current_node = $node_storage->load($current_id);
|
|
if (!$current_node instanceof NodeInterface ||
|
|
!$current_node->hasField('field_parent_page') ||
|
|
$current_node->get('field_parent_page')->isEmpty()) {
|
|
break;
|
|
}
|
|
|
|
$parent_field = $current_node->get('field_parent_page')->first();
|
|
// Only continue checking if parent is also a node.
|
|
if (!$parent_field || ($parent_field->target_type ?? NULL) !== 'node') {
|
|
break;
|
|
}
|
|
|
|
$current_id = $parent_field->target_id;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* Implements hook_theme().
|
|
*/
|
|
function structural_pages_theme(): array {
|
|
return [
|
|
'structural_pages_menu' => [
|
|
'variables' => [
|
|
'ancestor' => NULL,
|
|
'ancestor_url' => '',
|
|
'tree' => [],
|
|
'active_trail' => [],
|
|
'show_ancestor_title' => TRUE,
|
|
],
|
|
],
|
|
'structural_pages_menu_tree' => [
|
|
'variables' => [
|
|
'items' => [],
|
|
'active_trail' => [],
|
|
'depth' => 0,
|
|
],
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Implements hook_token_info().
|
|
*/
|
|
function structural_pages_token_info(): array {
|
|
$info = [];
|
|
|
|
$info['tokens']['node']['site-section-path'] = [
|
|
'name' => t('Site Section Path'),
|
|
'description' => t('The hierarchical path of the site section taxonomy (e.g., undergraduate/courses).'),
|
|
];
|
|
|
|
$info['tokens']['term']['hierarchy-path'] = [
|
|
'name' => t('Hierarchy Path'),
|
|
'description' => t('The hierarchical path of the term including ancestors (e.g., institutional/news).'),
|
|
];
|
|
|
|
return $info;
|
|
}
|
|
|
|
/**
|
|
* Implements hook_tokens().
|
|
*/
|
|
function structural_pages_tokens(string $type, array $tokens, array $data, array $options, $bubbleable_metadata): array {
|
|
$replacements = [];
|
|
|
|
if ($type === 'node' && !empty($data['node'])) {
|
|
$node = $data['node'];
|
|
foreach ($tokens as $name => $original) {
|
|
if ($name === 'site-section-path') {
|
|
$replacements[$original] = _structural_pages_get_section_path($node);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($type === 'term' && !empty($data['term'])) {
|
|
$term = $data['term'];
|
|
foreach ($tokens as $name => $original) {
|
|
if ($name === 'hierarchy-path') {
|
|
$replacements[$original] = _structural_pages_get_term_hierarchy_path($term);
|
|
}
|
|
}
|
|
}
|
|
|
|
return $replacements;
|
|
}
|
|
|
|
/**
|
|
* Gets the hierarchical path of the site section for a node.
|
|
*
|
|
* @param \Drupal\node\NodeInterface $node
|
|
* The node.
|
|
*
|
|
* @return string
|
|
* The section path (e.g., "undergraduate/courses") or empty string.
|
|
*/
|
|
function _structural_pages_get_section_path(NodeInterface $node): string {
|
|
if (!$node->hasField('field_site_section') || $node->get('field_site_section')->isEmpty()) {
|
|
return '';
|
|
}
|
|
|
|
$term_id = $node->get('field_site_section')->target_id;
|
|
$term_storage = \Drupal::entityTypeManager()->getStorage('taxonomy_term');
|
|
$term = $term_storage->load($term_id);
|
|
|
|
if (!$term) {
|
|
return '';
|
|
}
|
|
|
|
// Get all ancestors of the term.
|
|
$ancestors = $term_storage->loadAllParents($term_id);
|
|
$ancestors = array_reverse($ancestors);
|
|
|
|
// Build the path using the term names converted to URL.
|
|
$path_parts = [];
|
|
foreach ($ancestors as $ancestor) {
|
|
$path_parts[] = \Drupal::service('pathauto.alias_cleaner')->cleanString($ancestor->getName());
|
|
}
|
|
|
|
return implode('/', $path_parts);
|
|
}
|
|
|
|
/**
|
|
* Gets the hierarchical path of a taxonomy term.
|
|
*
|
|
* @param \Drupal\taxonomy\TermInterface $term
|
|
* The taxonomy term.
|
|
*
|
|
* @return string
|
|
* The hierarchical path (e.g., "institutional/news") or empty string.
|
|
*/
|
|
function _structural_pages_get_term_hierarchy_path(TermInterface $term): string {
|
|
$term_storage = \Drupal::entityTypeManager()->getStorage('taxonomy_term');
|
|
|
|
// Get all ancestors of the term (including itself).
|
|
$ancestors = $term_storage->loadAllParents($term->id());
|
|
$ancestors = array_reverse($ancestors);
|
|
|
|
// Build the path using the term names converted to URL.
|
|
$path_parts = [];
|
|
foreach ($ancestors as $ancestor) {
|
|
$path_parts[] = \Drupal::service('pathauto.alias_cleaner')->cleanString($ancestor->getName());
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
// 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;
|
|
|
|
// 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('- 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'],
|
|
'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 = [];
|
|
|
|
$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.
|
|
$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);
|
|
}
|
|
}
|
|
|
|
$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 = [];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|