mirror of
https://gitlab.unicamp.br/infimecc_drupal11_modules/structural_pages.git
synced 2026-05-05 13:30: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).'),
|
'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'] = [
|
$info['tokens']['term']['hierarchy-path'] = [
|
||||||
'name' => t('Hierarchy Path'),
|
'name' => t('Hierarchy Path'),
|
||||||
'description' => t('The hierarchical path of the term including ancestors (e.g., institutional/news).'),
|
'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') {
|
if ($name === 'site-section-path') {
|
||||||
$replacements[$original] = _structural_pages_get_section_path($node);
|
$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);
|
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.
|
* Gets the hierarchical path of a taxonomy term.
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user