mirror of
https://gitlab.unicamp.br/infimecc_drupal11_modules/structural_pages.git
synced 2026-05-04 13:10:41 -03:00
Adiciona token [node:content-page-ancestors] para Pathauto
Registra token do tipo array que resolve o caminho hierárquico completo de um content_page: domínio (alias de user/term/etc.) + slugs dos nós pai, do mais distante ao mais próximo. Permite padrão Pathauto como "[node:content-page-ancestors:join-path]/[node:title]". Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -158,6 +158,12 @@ function structural_pages_token_info(): array {
|
||||
'description' => t('The hierarchical path of the site section taxonomy (e.g., undergraduate/courses).'),
|
||||
];
|
||||
|
||||
$info['tokens']['node']['content-page-ancestors'] = [
|
||||
'name' => t('Content Page Ancestors Path'),
|
||||
'description' => t('Array of ancestor path segments for a content_page node (domain + parent pages). Use [node:content-page-ancestors:join-path] in Pathauto patterns to get a slash-separated path (e.g., user/joao/pesquisa).'),
|
||||
'type' => 'array',
|
||||
];
|
||||
|
||||
$info['tokens']['term']['hierarchy-path'] = [
|
||||
'name' => t('Hierarchy Path'),
|
||||
'description' => t('The hierarchical path of the term including ancestors (e.g., institutional/news).'),
|
||||
@@ -178,6 +184,30 @@ function structural_pages_tokens(string $type, array $tokens, array $data, array
|
||||
if ($name === 'site-section-path') {
|
||||
$replacements[$original] = _structural_pages_get_section_path($node);
|
||||
}
|
||||
if ($name === 'content-page-ancestors') {
|
||||
$path = _structural_pages_get_content_page_ancestors($node);
|
||||
$replacements[$original] = !empty($path)
|
||||
? array_values(array_filter(explode('/', $path)))
|
||||
: '';
|
||||
}
|
||||
}
|
||||
|
||||
// Handle [node:content-page-ancestors:join-path] and other chained tokens.
|
||||
// Pathauto's [array:join-path] cleans each segment individually and joins
|
||||
// with '/', preserving slashes. The token ends in ':join-path]' which is
|
||||
// in Pathauto's safe_tokens list, so the result is NOT re-cleaned.
|
||||
if ($ancestors_tokens = \Drupal::token()->findWithPrefix($tokens, 'content-page-ancestors')) {
|
||||
$path = _structural_pages_get_content_page_ancestors($node);
|
||||
if (!empty($path)) {
|
||||
$segments = array_values(array_filter(explode('/', $path)));
|
||||
$replacements += \Drupal::token()->generate(
|
||||
'array',
|
||||
$ancestors_tokens,
|
||||
['array' => $segments],
|
||||
$options,
|
||||
$bubbleable_metadata
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,6 +264,109 @@ function _structural_pages_get_section_path(NodeInterface $node): string {
|
||||
return implode('/', $path_parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the full ancestor path for a content_page node.
|
||||
*
|
||||
* Traverses the field_parent_page chain to the root, then prepends the domain
|
||||
* entity's URL alias (from field_site_section). Intended as a Pathauto token
|
||||
* prefix: configure the pattern as "[node:content-page-ancestors]/[node:title]".
|
||||
*
|
||||
* Examples:
|
||||
* - Root page under user "joao" → "user/joao"
|
||||
* - Child of "Pesquisa" under user "joao" → "user/joao/pesquisa"
|
||||
* - Root page under term "graduacao/disciplinas" → "graduacao/disciplinas"
|
||||
*
|
||||
* @param \Drupal\node\NodeInterface $node
|
||||
* The content_page node.
|
||||
*
|
||||
* @return string
|
||||
* The ancestor path without leading/trailing slashes, or empty string.
|
||||
*/
|
||||
function _structural_pages_get_content_page_ancestors(NodeInterface $node): string {
|
||||
if ($node->bundle() !== 'content_page') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$node_storage = \Drupal::entityTypeManager()->getStorage('node');
|
||||
$alias_cleaner = \Drupal::service('pathauto.alias_cleaner');
|
||||
$path_alias_manager = \Drupal::service('path_alias.manager');
|
||||
|
||||
// Walk up the field_parent_page chain collecting parent titles.
|
||||
$parent_slugs = [];
|
||||
$visited = [];
|
||||
$current = $node;
|
||||
$max = 50;
|
||||
|
||||
while ($max-- > 0) {
|
||||
if (isset($visited[$current->id()])) {
|
||||
break;
|
||||
}
|
||||
$visited[$current->id()] = TRUE;
|
||||
|
||||
if (!$current->hasField('field_parent_page') || $current->get('field_parent_page')->isEmpty()) {
|
||||
// Reached the root content_page: resolve domain from field_site_section.
|
||||
if (!$current->hasField('field_site_section') || $current->get('field_site_section')->isEmpty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
$fss = $current->get('field_site_section')->first();
|
||||
$domain_type = $fss->target_type ?? 'taxonomy_term';
|
||||
$domain_id = $fss->target_id;
|
||||
$domain = \Drupal::entityTypeManager()->getStorage($domain_type)->load($domain_id);
|
||||
|
||||
if (!$domain || !$domain->hasLinkTemplate('canonical')) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Build the system path for the domain entity.
|
||||
// Constructed directly for known entity types to avoid URL generator
|
||||
// context issues that can occur inside hook_tokens().
|
||||
$domain_system_path = match ($domain_type) {
|
||||
'user' => '/user/' . $domain_id,
|
||||
'taxonomy_term' => '/taxonomy/term/' . $domain_id,
|
||||
default => (static function () use ($domain): string {
|
||||
try {
|
||||
return '/' . $domain->toUrl('canonical')->getInternalPath();
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
return '';
|
||||
}
|
||||
})(),
|
||||
};
|
||||
|
||||
if (empty($domain_system_path)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Resolve to alias if one exists.
|
||||
$domain_alias = $path_alias_manager->getAliasByPath($domain_system_path);
|
||||
$domain_path = ltrim($domain_alias, '/');
|
||||
|
||||
// Build: domain/parent2/parent1 (parents were collected innermost-first).
|
||||
$parts = array_merge([$domain_path], array_reverse($parent_slugs));
|
||||
return implode('/', array_filter($parts));
|
||||
}
|
||||
|
||||
// Collect this node's parent's title (we traverse before adding current).
|
||||
$parent_field = $current->get('field_parent_page')->first();
|
||||
$parent_id = $parent_field->target_id ?? NULL;
|
||||
if (!$parent_id) {
|
||||
break;
|
||||
}
|
||||
|
||||
$parent = $node_storage->load($parent_id);
|
||||
if (!$parent) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Add the parent's slug to the list (innermost first, reversed later).
|
||||
$parent_slugs[] = $alias_cleaner->cleanString($parent->getTitle());
|
||||
$current = $parent;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the hierarchical path of a taxonomy term.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user