diff --git a/config/install/field.field.node.content_page.field_parent_page.yml b/config/install/field.field.node.content_page.field_parent_page.yml index 03a09f5..563d7fd 100644 --- a/config/install/field.field.node.content_page.field_parent_page.yml +++ b/config/install/field.field.node.content_page.field_parent_page.yml @@ -5,7 +5,7 @@ dependencies: - field.storage.node.field_parent_page - node.type.content_page - node.type.section_page - - taxonomy.vocabulary.site_section + - taxonomy.vocabulary.site_sections module: - dynamic_entity_reference id: node.content_page.field_parent_page @@ -36,7 +36,7 @@ settings: handler: 'default:taxonomy_term' handler_settings: target_bundles: - site_section: site_section + site_sections: site_sections sort: field: name direction: asc diff --git a/config/install/field.field.node.content_page.field_site_section.yml b/config/install/field.field.node.content_page.field_site_section.yml index 8bee4e5..9072e4f 100644 --- a/config/install/field.field.node.content_page.field_site_section.yml +++ b/config/install/field.field.node.content_page.field_site_section.yml @@ -4,7 +4,7 @@ dependencies: config: - field.storage.node.field_site_section - node.type.content_page - - taxonomy.vocabulary.site_section + - taxonomy.vocabulary.site_sections module: - taxonomy id: node.content_page.field_site_section @@ -21,7 +21,7 @@ settings: handler: 'default:taxonomy_term' handler_settings: target_bundles: - site_section: site_section + site_sections: site_sections sort: field: name direction: asc diff --git a/config/install/field.field.node.section_page.field_site_section.yml b/config/install/field.field.node.section_page.field_site_section.yml index e6b3047..a556115 100644 --- a/config/install/field.field.node.section_page.field_site_section.yml +++ b/config/install/field.field.node.section_page.field_site_section.yml @@ -4,7 +4,7 @@ dependencies: config: - field.storage.node.field_site_section - node.type.section_page - - taxonomy.vocabulary.site_section + - taxonomy.vocabulary.site_sections module: - taxonomy id: node.section_page.field_site_section @@ -21,7 +21,7 @@ settings: handler: 'default:taxonomy_term' handler_settings: target_bundles: - site_section: site_section + site_sections: site_sections sort: field: name direction: asc diff --git a/config/install/pathauto.pattern.content_page.yml b/config/install/pathauto.pattern.content_page.yml index 71b8884..900accd 100644 --- a/config/install/pathauto.pattern.content_page.yml +++ b/config/install/pathauto.pattern.content_page.yml @@ -3,7 +3,7 @@ status: true dependencies: module: - node - - site_structure + - structural_pages id: content_page label: 'Content Page' type: 'canonical_entities:node' diff --git a/config/install/pathauto.pattern.section_page.yml b/config/install/pathauto.pattern.section_page.yml index a863f86..183b492 100644 --- a/config/install/pathauto.pattern.section_page.yml +++ b/config/install/pathauto.pattern.section_page.yml @@ -3,7 +3,7 @@ status: true dependencies: module: - node - - site_structure + - structural_pages id: section_page label: 'Section Page' type: 'canonical_entities:node' diff --git a/config/install/pathauto.pattern.site_section_term.yml b/config/install/pathauto.pattern.site_sections_term.yml similarity index 76% rename from config/install/pathauto.pattern.site_section_term.yml rename to config/install/pathauto.pattern.site_sections_term.yml index 3c1d1cf..7a926be 100644 --- a/config/install/pathauto.pattern.site_section_term.yml +++ b/config/install/pathauto.pattern.site_sections_term.yml @@ -2,10 +2,10 @@ langcode: en status: true dependencies: module: - - site_structure + - structural_pages - taxonomy -id: site_section_term -label: 'Site Section Term' +id: site_sections_term +label: 'Site Sections Term' type: 'canonical_entities:taxonomy_term' pattern: '[term:hierarchy-path]' selection_logic: and @@ -16,5 +16,5 @@ selection_criteria: context_mapping: taxonomy_term: taxonomy_term bundles: - site_section: site_section + site_sections: site_sections weight: 0 diff --git a/config/install/site_structure.settings.yml b/config/install/structural_pages.settings.yml similarity index 89% rename from config/install/site_structure.settings.yml rename to config/install/structural_pages.settings.yml index 0fc0874..a1e90bf 100644 --- a/config/install/site_structure.settings.yml +++ b/config/install/structural_pages.settings.yml @@ -4,7 +4,7 @@ allowed_parent_targets: - entity_type: node bundle: section_page - entity_type: taxonomy_term - bundle: site_section + bundle: site_sections - entity_type: user bundle: user - entity_type: group diff --git a/config/install/taxonomy.vocabulary.site_section.yml b/config/install/taxonomy.vocabulary.site_sections.yml similarity index 74% rename from config/install/taxonomy.vocabulary.site_section.yml rename to config/install/taxonomy.vocabulary.site_sections.yml index e6ceddd..c4e459f 100644 --- a/config/install/taxonomy.vocabulary.site_section.yml +++ b/config/install/taxonomy.vocabulary.site_sections.yml @@ -1,7 +1,7 @@ langcode: en status: true dependencies: { } -name: 'Site Section' -vid: site_section +name: 'Site Sections' +vid: site_sections description: 'Main hierarchical structure for site organization.' weight: 0 diff --git a/config/install/views.view.child_pages.yml b/config/install/views.view.child_pages.yml index c3566be..dc5c878 100644 --- a/config/install/views.view.child_pages.yml +++ b/config/install/views.view.child_pages.yml @@ -240,7 +240,7 @@ display: display_options: display_extenders: { } block_description: 'Child Pages' - block_category: 'Site Structure' + block_category: 'Structural Pages' cache_metadata: max-age: -1 contexts: diff --git a/config/schema/site_structure.schema.yml b/config/schema/structural_pages.schema.yml similarity index 83% rename from config/schema/site_structure.schema.yml rename to config/schema/structural_pages.schema.yml index fcd2b66..8026e38 100644 --- a/config/schema/site_structure.schema.yml +++ b/config/schema/structural_pages.schema.yml @@ -1,6 +1,6 @@ -site_structure.settings: +structural_pages.settings: type: config_object - label: 'Site Structure settings' + label: 'Structural Pages settings' mapping: allowed_parent_targets: type: sequence diff --git a/css/site-structure-menu.css b/css/structural-pages-menu.css similarity index 52% rename from css/site-structure-menu.css rename to css/structural-pages-menu.css index b0f3521..670a99a 100644 --- a/css/site-structure-menu.css +++ b/css/structural-pages-menu.css @@ -1,45 +1,45 @@ /** * @file - * Styles for the site structure menu block. + * Styles for the structural pages menu block. */ -.site-structure-menu { +.structural-pages-menu { font-size: 0.9rem; } -.site-structure-menu__title { +.structural-pages-menu__title { font-size: 1.1rem; margin-bottom: 0.75rem; padding-bottom: 0.5rem; border-bottom: 1px solid #ddd; } -.site-structure-menu__title a { +.structural-pages-menu__title a { text-decoration: none; color: inherit; } -.site-structure-menu__title a:hover { +.structural-pages-menu__title a:hover { text-decoration: underline; } -.site-structure-menu__list { +.structural-pages-menu__list { list-style: none; margin: 0; padding: 0; } -.site-structure-menu__list--level-2, -.site-structure-menu__list--level-3, -.site-structure-menu__list--level-4 { +.structural-pages-menu__list--level-2, +.structural-pages-menu__list--level-3, +.structural-pages-menu__list--level-4 { padding-left: 1rem; } -.site-structure-menu__item { +.structural-pages-menu__item { margin: 0.25rem 0; } -.site-structure-menu__link { +.structural-pages-menu__link { display: block; padding: 0.25rem 0.5rem; text-decoration: none; @@ -48,16 +48,16 @@ transition: background-color 0.15s ease; } -.site-structure-menu__link:hover { +.structural-pages-menu__link:hover { background-color: #f5f5f5; text-decoration: none; } -.site-structure-menu__link--active-trail { +.structural-pages-menu__link--active-trail { font-weight: 600; color: #0073bd; } -.site-structure-menu__item--active-trail > .site-structure-menu__link { +.structural-pages-menu__item--active-trail > .structural-pages-menu__link { background-color: #e8f4fc; } diff --git a/docs/DESIGN.md b/docs/DESIGN.md index b9a4533..e869604 100644 --- a/docs/DESIGN.md +++ b/docs/DESIGN.md @@ -1,8 +1,8 @@ -# Site Structure - Design Document +# Structural Pages - Design Document ## Overview -The `site_structure` module implements a hierarchical editorial structure and contextual navigation for institutional sites in Drupal 11. The module was designed for higher education institutions but is generic enough for other institutional contexts. +The `structural_pages` module implements a hierarchical editorial structure and contextual navigation for institutional sites in Drupal 11. The module was designed for higher education institutions but is generic enough for other institutional contexts. ## Problem @@ -21,7 +21,7 @@ The module implements two complementary structures: ``` ┌─────────────────────────────────────────────────────────────────────┐ -│ PRIMARY STRUCTURE: site_section Taxonomy │ +│ PRIMARY STRUCTURE: site_sections Taxonomy │ │ │ │ - Governs the overall organization of site information │ │ - Defines breadcrumbs and URLs (via Pathauto) │ @@ -48,12 +48,12 @@ The module implements two complementary structures: ## Components -### 1. Taxonomy Vocabulary: `site_section` +### 1. Taxonomy Vocabulary: `site_sections` | Property | Value | |----------|-------| -| Machine name | `site_section` | -| Name | Site Section | +| Machine name | `site_sections` | +| Name | Site Sections | | Hierarchical | Yes | | Description | Main hierarchical structure for site organization | @@ -104,7 +104,7 @@ The `field_parent_page` uses the Dynamic Entity Reference module to support mult **Default allowed targets**: - `node:content_page` - Other content pages - `node:section_page` - Section pages -- `taxonomy_term:site_section` - Site section terms +- `taxonomy_term:site_sections` - Site section terms - `user:user` - User accounts - `group:*` - All group types (requires Group module) @@ -113,7 +113,7 @@ The `field_parent_page` uses the Dynamic Entity Reference module to support mult - **User**: Content pages are associated with the user profile page (`/user/{id}`). The `field_site_section` is cleared as the context is the user itself. - **Group**: Content pages are associated with the group. The `field_site_section` is cleared as the context is the group itself. -**Configuration**: `/admin/config/local-modules/site-structure` +**Configuration**: `/admin/config/local-modules/structural-pages` ### 5. Inheritance Logic @@ -124,7 +124,7 @@ When a content_page is saved: IF field_parent_page is filled: IF parent is user or group: Clear field_site_section (context is the entity itself) - ELSE IF parent is taxonomy_term (site_section): + ELSE IF parent is taxonomy_term (site_sections): Set field_site_section to the parent term ID ELSE IF parent is node with field_site_section: Copy field_site_section from parent node @@ -181,12 +181,12 @@ View that lists child pages of the current `content_page`. ### 9. Settings Form -Configuration form at `/admin/config/local-modules/site-structure` that allows administrators to: +Configuration form at `/admin/config/local-modules/structural-pages` that allows administrators to: - Select which node bundles can be used as parents - Select which taxonomy vocabularies can be used as parents - Changes automatically update the field configuration -### 10. Block: Site Structure Menu +### 10. Block: Structural Pages Menu Dynamic menu block that renders hierarchical navigation based on the content structure. @@ -221,7 +221,7 @@ Documentation (ancestor: taxonomy term) **Usage**: 1. Navigate to Structure > Block layout -2. Place "Site Structure Menu" block in desired region +2. Place "Structural Pages Menu" block in desired region 3. Configure visibility (e.g., show only on content_page nodes) 4. Adjust depth and display options as needed @@ -230,19 +230,19 @@ Documentation (ancestor: taxonomy term) ## File Structure ``` -site_structure/ -├── site_structure.info.yml # Metadata and dependencies -├── site_structure.module # Hooks (presave, tokens, theme) -├── site_structure.install # Installation hooks -├── site_structure.services.yml # Service registration -├── site_structure.routing.yml # Route definitions -├── site_structure.links.menu.yml # Admin menu links -├── site_structure.libraries.yml # Asset libraries (CSS) +structural_pages/ +├── structural_pages.info.yml # Metadata and dependencies +├── structural_pages.module # Hooks (presave, tokens, theme) +├── structural_pages.install # Installation hooks +├── structural_pages.services.yml # Service registration +├── structural_pages.routing.yml # Route definitions +├── structural_pages.links.menu.yml # Admin menu links +├── structural_pages.libraries.yml # Asset libraries (CSS) │ ├── config/ │ ├── install/ -│ │ ├── site_structure.settings.yml # Default settings -│ │ ├── taxonomy.vocabulary.site_section.yml +│ │ ├── structural_pages.settings.yml # Default settings +│ │ ├── taxonomy.vocabulary.site_sections.yml │ │ │ │ │ ├── node.type.section_page.yml │ │ ├── field.storage.node.field_site_section.yml @@ -259,18 +259,18 @@ site_structure/ │ │ │ │ │ ├── pathauto.pattern.section_page.yml │ │ ├── pathauto.pattern.content_page.yml -│ │ ├── pathauto.pattern.site_section_term.yml +│ │ ├── pathauto.pattern.site_sections_term.yml │ │ │ │ │ └── views.view.child_pages.yml │ │ │ └── schema/ -│ └── site_structure.schema.yml # Config schema +│ └── structural_pages.schema.yml # Config schema │ ├── css/ -│ └── site-structure-menu.css # Menu block styles +│ └── structural-pages-menu.css # Menu block styles │ ├── templates/ -│ └── site-structure-menu.html.twig # Menu block template +│ └── structural-pages-menu.html.twig # Menu block template │ ├── translations/ │ └── pt-br.po # Portuguese (Brazil) translation @@ -289,11 +289,11 @@ site_structure/ │ │ └── SectionBreadcrumbBuilder.php │ │ │ ├── Form/ -│ │ └── SiteStructureSettingsForm.php +│ │ └── StructuralPagesSettingsForm.php │ │ │ └── Plugin/ │ ├── Block/ -│ │ └── SiteStructureMenuBlock.php # Dynamic menu block +│ │ └── StructuralPagesMenuBlock.php # Dynamic menu block │ │ │ └── ParentEntityHandler/ # Built-in handlers │ ├── TaxonomyTermHandler.php # taxonomy_term handler @@ -301,8 +301,8 @@ site_structure/ │ └── NodeHandler.php # node handler │ ├── modules/ -│ └── site_structure_group/ # Group integration submodule -│ ├── site_structure_group.info.yml +│ └── structural_pages_group/ # Group integration submodule +│ ├── structural_pages_group.info.yml │ └── src/Plugin/ParentEntityHandler/ │ └── GroupHandler.php # group handler │ @@ -346,7 +346,7 @@ site_structure/ 1. **Create root page (anchored to taxonomy term)**: - Add content > Content Page - - In "Parent Page", select a site_section term + - In "Parent Page", select a site_sections term - Site section is set automatically from the term - Save @@ -370,7 +370,7 @@ site_structure/ ### Configuring Allowed Parent Types -1. Navigate to `/admin/config/local-modules/site-structure` +1. Navigate to `/admin/config/local-modules/structural-pages` 2. Select which content types (nodes) can be used as parents 3. Select which taxonomy vocabularies can be used as parents 4. Save configuration @@ -386,7 +386,7 @@ The module uses a plugin system to support different entity types as parents for The plugin system consists of: -1. **Attribute**: `Drupal\site_structure\Attribute\ParentEntityHandler` - PHP 8 attribute for defining handlers +1. **Attribute**: `Drupal\structural_pages\Attribute\ParentEntityHandler` - PHP 8 attribute for defining handlers 2. **Interface**: `ParentEntityHandlerInterface` - Contract for all handlers 3. **Base class**: `ParentEntityHandlerBase` - Common implementation 4. **Manager**: `ParentEntityHandlerManager` - Plugin discovery and aggregation @@ -395,7 +395,7 @@ The plugin system consists of: | Handler | Entity Type | Clears Site Section | Sort Field | Bundle Restrictions | |---------|-------------|---------------------|------------|---------------------| -| TaxonomyTermHandler | taxonomy_term | No | name | site_section | +| TaxonomyTermHandler | taxonomy_term | No | name | site_sections | | UserHandler | user | Yes | name | - | | NodeHandler | node | No | title | content_page, section_page | @@ -409,8 +409,8 @@ To add support for a new entity type, create a handler plugin: namespace Drupal\my_module\Plugin\ParentEntityHandler; use Drupal\Core\StringTranslation\TranslatableMarkup; -use Drupal\site_structure\Attribute\ParentEntityHandler; -use Drupal\site_structure\ParentEntityHandler\ParentEntityHandlerBase; +use Drupal\structural_pages\Attribute\ParentEntityHandler; +use Drupal\structural_pages\ParentEntityHandler\ParentEntityHandlerBase; #[ParentEntityHandler( id: 'my_entity', @@ -442,7 +442,7 @@ class MyEntityHandler extends ParentEntityHandlerBase { ## Group Integration -Group support is provided via the `site_structure_group` submodule, which adds a handler for group entities. This submodule requires the Group module. +Group support is provided via the `structural_pages_group` submodule, which adds a handler for group entities. This submodule requires the Group module. **Features**: - Content pages can have groups as parent entities @@ -451,15 +451,15 @@ Group support is provided via the `site_structure_group` submodule, which adds a - `field_site_section` is cleared for group-parented content (context is the group) - Per-section permissions can be implemented via Groups -**Installation**: Enable the `site_structure_group` submodule after installing the Group module. +**Installation**: Enable the `structural_pages_group` submodule after installing the Group module. -**Configuration**: Enable group types in the settings form at `/admin/config/local-modules/site-structure` +**Configuration**: Enable group types in the settings form at `/admin/config/local-modules/structural-pages` --- ## Installation Verification -1. `drush en site_structure -y` +1. `drush en structural_pages -y` 2. Verify vocabulary at `/admin/structure/taxonomy` 3. Verify content types at `/admin/structure/types` 4. Create test hierarchical terms @@ -467,7 +467,7 @@ Group support is provided via the `site_structure_group` submodule, which adds a 6. Create root `content_page` with taxonomy term as parent 7. Create child `content_page`, verify inheritance 8. Verify child pages View -9. Test settings form at `/admin/config/local-modules/site-structure` +9. Test settings form at `/admin/config/local-modules/structural-pages` --- @@ -478,5 +478,5 @@ Group support is provided via the `site_structure_group` submodule, which adds a | 1.0.0 | - | Initial version | | 1.1.0 | - | Added dynamic_entity_reference support for multi-type parents | | 1.2.0 | - | Added user and group entity support as parent types | -| 1.3.0 | - | Added Site Structure Menu block for dynamic hierarchical navigation | +| 1.3.0 | - | Added Structural Pages Menu block for dynamic hierarchical navigation | | 2.0.0 | - | Refactored to plugin system for parent entity handlers. Group support moved to submodule. | diff --git a/docs/PLUGIN_SYSTEM_PLAN.md b/docs/PLUGIN_SYSTEM_PLAN.md new file mode 100644 index 0000000..283f0f2 --- /dev/null +++ b/docs/PLUGIN_SYSTEM_PLAN.md @@ -0,0 +1,237 @@ +# Plano: Sistema de Plugins ParentEntityHandler + +## Problema + +O módulo structural_pages tem dependências hardcoded para diferentes tipos de entidade pai (taxonomy_term, user, group, node) espalhadas em múltiplos arquivos: + +- `StructuralPagesSettingsForm.php:46-51` - Array hardcoded `$supportedEntityTypes` +- `structural_pages.module:55-59` - `in_array($parent_entity_type, ['user', 'group'])` +- `StructuralPagesMenuBlock.php:14` - `use Drupal\group\Entity\GroupInterface;` (causa erro sem o módulo group) +- `StructuralPagesMenuBlock.php:274-290` - Verificações `instanceof` para cada tipo +- `SectionBreadcrumbBuilder.php:82-100` - Switch statement para tipos de contexto + +**Consequência:** Adicionar um novo tipo de entidade requer modificar múltiplos arquivos, e o import do GroupInterface causa erro quando o módulo group não está instalado. + +## Solução + +Implementar um sistema de plugins Drupal (Attribute-based para D10+) onde cada handler define: +- Tipo de entidade que gerencia +- Módulo provedor (para dependências opcionais) +- Como detectar a entidade na rota +- Como construir breadcrumbs +- Se deve limpar `field_site_section` +- Campo de ordenação + +--- + +## Nova Estrutura de Arquivos + +``` +structural_pages/ +├── src/ +│ ├── Attribute/ +│ │ └── ParentEntityHandler.php # NOVO +│ ├── ParentEntityHandler/ +│ │ ├── ParentEntityHandlerInterface.php # NOVO +│ │ ├── ParentEntityHandlerManagerInterface.php # NOVO +│ │ ├── ParentEntityHandlerBase.php # NOVO +│ │ └── ParentEntityHandlerManager.php # NOVO +│ ├── Plugin/ +│ │ ├── Block/ +│ │ │ └── StructuralPagesMenuBlock.php # REFATORAR +│ │ └── ParentEntityHandler/ # NOVO +│ │ ├── TaxonomyTermHandler.php # NOVO +│ │ ├── UserHandler.php # NOVO +│ │ └── NodeHandler.php # NOVO +│ ├── Breadcrumb/ +│ │ └── SectionBreadcrumbBuilder.php # REFATORAR +│ └── Form/ +│ └── StructuralPagesSettingsForm.php # REFATORAR +├── modules/ +│ └── structural_pages_group/ # NOVO (submódulo) +│ ├── structural_pages_group.info.yml +│ └── src/Plugin/ParentEntityHandler/ +│ └── GroupHandler.php +├── structural_pages.module # REFATORAR +└── structural_pages.services.yml # ATUALIZAR +``` + +--- + +## Componentes Principais + +### 1. Attribute (src/Attribute/ParentEntityHandler.php) + +```php +#[\Attribute(\Attribute::TARGET_CLASS)] +class ParentEntityHandler extends Plugin { + public function __construct( + public readonly string $id, + public readonly TranslatableMarkup $label, + public readonly string $entity_type_id, + public readonly ?string $provider_module = NULL, + public readonly bool $clears_site_section = FALSE, + public readonly string $sort_field = 'title', + public readonly ?string $route_parameter = NULL, + public readonly array $bundle_restrictions = [], + public readonly int $weight = 0, + ) {} +} +``` + +### 2. Interface (src/ParentEntityHandler/ParentEntityHandlerInterface.php) + +Métodos principais: +- `getEntityTypeId(): string` +- `isAvailable(): bool` +- `clearsSiteSection(): bool` +- `getSortField(): string` +- `getEntityFromRoute(RouteMatchInterface): ?EntityInterface` +- `handlesEntity(EntityInterface): bool` +- `buildBreadcrumb(Breadcrumb, EntityInterface): void` +- `getSiteSectionId(EntityInterface): int|string|null` + +### 3. Manager (src/ParentEntityHandler/ParentEntityHandlerManager.php) + +Métodos principais: +- `getAvailableHandlers(): array` - Retorna handlers com módulos instalados +- `getHandlerForEntityType(string): ?ParentEntityHandlerInterface` +- `getSupportedEntityTypes(): array` - Para o formulário de configurações +- `clearsSiteSection(string): bool` +- `getSortField(string): string` +- `getEntityFromRoute(RouteMatchInterface): ?EntityInterface` +- `buildBreadcrumbForEntity(Breadcrumb, EntityInterface): bool` + +### 4. Handlers Built-in + +| Handler | entity_type_id | clears_site_section | sort_field | bundle_restrictions | +|---------|---------------|---------------------|------------|---------------------| +| TaxonomyTermHandler | taxonomy_term | false | name | ['site_sections'] | +| UserHandler | user | true | name | [] | +| NodeHandler | node | false | title | ['content_page', 'section_page'] | + +### 5. Submódulo structural_pages_group + +```yaml +# structural_pages_group.info.yml +name: 'Structural Pages Group Integration' +type: module +dependencies: + - structural_pages:structural_pages + - group:group +``` + +GroupHandler: `entity_type_id: group`, `clears_site_section: true`, `sort_field: label` + +--- + +## Refatorações + +### structural_pages.module (linhas 53-66) + +**Antes:** +```php +if (in_array($parent_entity_type, ['user', 'group'])) { + $entity->set('field_site_section', NULL); + return; +} +``` + +**Depois:** +```php +$handler_manager = \Drupal::service('plugin.manager.parent_entity_handler'); +if ($handler_manager->clearsSiteSection($parent_entity_type)) { + $entity->set('field_site_section', NULL); + return; +} +``` + +### StructuralPagesMenuBlock.php + +- Remover `use Drupal\group\Entity\GroupInterface;` +- Injetar `ParentEntityHandlerManagerInterface` +- Substituir `getAncestorFromRoute()`: + +```php +protected function getAncestorFromRoute(): ?EntityInterface { + return $this->handlerManager->getEntityFromRoute($this->routeMatch); +} +``` + +### SectionBreadcrumbBuilder.php + +- Injetar `ParentEntityHandlerManagerInterface` +- Substituir switch statement: + +```php +if ($context) { + $this->handlerManager->buildBreadcrumbForEntity($breadcrumb, $context['entity']); +} +``` + +- Remover métodos `addUserBreadcrumb()` e `addGroupBreadcrumb()` + +### StructuralPagesSettingsForm.php + +- Injetar `ParentEntityHandlerManagerInterface` +- Substituir `$supportedEntityTypes`: + +```php +$supportedEntityTypes = $this->handlerManager->getSupportedEntityTypes(); +``` + +- Substituir match statement de sort_field: + +```php +$sort_field = $this->handlerManager->getSortField($entity_type); +``` + +### structural_pages.services.yml + +```yaml +services: + plugin.manager.parent_entity_handler: + class: Drupal\structural_pages\ParentEntityHandler\ParentEntityHandlerManager + arguments: + - '@container.namespaces' + - '@cache.discovery' + - '@module_handler' + + structural_pages.breadcrumb.section: + class: Drupal\structural_pages\Breadcrumb\SectionBreadcrumbBuilder + arguments: + - '@entity_type.manager' + - '@plugin.manager.parent_entity_handler' + tags: + - { name: breadcrumb_builder, priority: 100 } +``` + +--- + +## Verificação + +1. **Sem módulo group instalado:** + - O módulo structural_pages deve funcionar normalmente + - Formulário de configurações não deve mostrar opção "Groups" + - Nenhum erro de classe não encontrada + +2. **Com submódulo structural_pages_group ativado:** + - Opção "Groups" aparece no formulário de configurações + - Groups funcionam como entidades pai + - Breadcrumbs mostram nome do grupo + +3. **Extensibilidade:** + - Criar handler de teste em módulo custom + - Verificar que é descoberto automaticamente pelo manager + +4. **Cache:** + - Limpar cache após mudanças (`drush cr`) + - Verificar que handlers são cacheados corretamente + +--- + +## Compatibilidade + +- Configuração existente (`allowed_parent_targets`) continua funcionando +- Nenhuma alteração no schema de configuração necessária +- Backwards compatible - apenas mudança interna de implementação diff --git a/modules/site_structure_group/src/Plugin/ParentEntityHandler/GroupHandler.php b/modules/structural_pages_group/src/Plugin/ParentEntityHandler/GroupHandler.php similarity index 77% rename from modules/site_structure_group/src/Plugin/ParentEntityHandler/GroupHandler.php rename to modules/structural_pages_group/src/Plugin/ParentEntityHandler/GroupHandler.php index d033082..8895a90 100644 --- a/modules/site_structure_group/src/Plugin/ParentEntityHandler/GroupHandler.php +++ b/modules/structural_pages_group/src/Plugin/ParentEntityHandler/GroupHandler.php @@ -2,19 +2,19 @@ declare(strict_types=1); -namespace Drupal\site_structure_group\Plugin\ParentEntityHandler; +namespace Drupal\structural_pages_group\Plugin\ParentEntityHandler; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\StringTranslation\TranslatableMarkup; use Drupal\group\Entity\GroupInterface; -use Drupal\site_structure\Attribute\ParentEntityHandler; -use Drupal\site_structure\ParentEntityHandler\ParentEntityHandlerBase; +use Drupal\structural_pages\Attribute\ParentEntityHandler; +use Drupal\structural_pages\ParentEntityHandler\ParentEntityHandlerBase; /** * Handler for group entities. * - * This handler is provided by the site_structure_group submodule and is only + * This handler is provided by the structural_pages_group submodule and is only * available when the Group module is installed. */ #[ParentEntityHandler( diff --git a/modules/site_structure_group/site_structure_group.info.yml b/modules/structural_pages_group/structural_pages_group.info.yml similarity index 52% rename from modules/site_structure_group/site_structure_group.info.yml rename to modules/structural_pages_group/structural_pages_group.info.yml index 1f0406e..78928d5 100644 --- a/modules/site_structure_group/site_structure_group.info.yml +++ b/modules/structural_pages_group/structural_pages_group.info.yml @@ -1,8 +1,8 @@ -name: 'Site Structure Group Integration' +name: 'Structural Pages Group Integration' type: module -description: 'Provides Group entity support as parent type for Site Structure module.' -package: 'Site Structure' +description: 'Provides Group entity support as parent type for Structural Pages module.' +package: 'Structural Pages' core_version_requirement: ^10.3 || ^11 dependencies: - - site_structure:site_structure + - structural_pages:structural_pages - group:group diff --git a/site_structure.routing.yml b/site_structure.routing.yml deleted file mode 100644 index 0f7b23e..0000000 --- a/site_structure.routing.yml +++ /dev/null @@ -1,7 +0,0 @@ -site_structure.settings: - path: '/admin/config/local-modules/site-structure' - defaults: - _form: '\Drupal\site_structure\Form\SiteStructureSettingsForm' - _title: 'Site Structure Settings' - requirements: - _permission: 'administer site configuration' diff --git a/src/Attribute/ParentEntityHandler.php b/src/Attribute/ParentEntityHandler.php index c949498..807007b 100644 --- a/src/Attribute/ParentEntityHandler.php +++ b/src/Attribute/ParentEntityHandler.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\site_structure\Attribute; +namespace Drupal\structural_pages\Attribute; use Drupal\Component\Plugin\Attribute\Plugin; use Drupal\Core\StringTranslation\TranslatableMarkup; @@ -12,9 +12,9 @@ use Drupal\Core\StringTranslation\TranslatableMarkup; * * Plugin namespace: Plugin\ParentEntityHandler. * - * @see \Drupal\site_structure\ParentEntityHandler\ParentEntityHandlerInterface - * @see \Drupal\site_structure\ParentEntityHandler\ParentEntityHandlerBase - * @see \Drupal\site_structure\ParentEntityHandler\ParentEntityHandlerManager + * @see \Drupal\structural_pages\ParentEntityHandler\ParentEntityHandlerInterface + * @see \Drupal\structural_pages\ParentEntityHandler\ParentEntityHandlerBase + * @see \Drupal\structural_pages\ParentEntityHandler\ParentEntityHandlerManager * @see plugin_api */ #[\Attribute(\Attribute::TARGET_CLASS)] @@ -51,7 +51,7 @@ class ParentEntityHandler extends Plugin { * (optional) The deriver class. */ public function __construct( - public readonly string $id, + string $id, public readonly TranslatableMarkup $label, public readonly string $entity_type_id, public readonly ?string $provider_module = NULL, @@ -60,7 +60,7 @@ class ParentEntityHandler extends Plugin { public readonly ?string $route_parameter = NULL, public readonly array $bundle_restrictions = [], public readonly int $weight = 0, - public readonly ?string $deriver = NULL, + ?string $deriver = NULL, ) { parent::__construct($id, $deriver); } diff --git a/src/Breadcrumb/SectionBreadcrumbBuilder.php b/src/Breadcrumb/SectionBreadcrumbBuilder.php index b9eab16..355c4c8 100644 --- a/src/Breadcrumb/SectionBreadcrumbBuilder.php +++ b/src/Breadcrumb/SectionBreadcrumbBuilder.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\site_structure\Breadcrumb; +namespace Drupal\structural_pages\Breadcrumb; use Drupal\Core\Breadcrumb\Breadcrumb; use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface; @@ -11,7 +11,7 @@ use Drupal\Core\Link; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\node\NodeInterface; -use Drupal\site_structure\ParentEntityHandler\ParentEntityHandlerManagerInterface; +use Drupal\structural_pages\ParentEntityHandler\ParentEntityHandlerManagerInterface; /** * Provides a breadcrumb builder for section_page and content_page content types. @@ -30,7 +30,7 @@ class SectionBreadcrumbBuilder implements BreadcrumbBuilderInterface { /** * The parent entity handler manager. * - * @var \Drupal\site_structure\ParentEntityHandler\ParentEntityHandlerManagerInterface + * @var \Drupal\structural_pages\ParentEntityHandler\ParentEntityHandlerManagerInterface */ protected ParentEntityHandlerManagerInterface $handlerManager; @@ -46,7 +46,7 @@ class SectionBreadcrumbBuilder implements BreadcrumbBuilderInterface { * * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager * The entity type manager. - * @param \Drupal\site_structure\ParentEntityHandler\ParentEntityHandlerManagerInterface $handler_manager + * @param \Drupal\structural_pages\ParentEntityHandler\ParentEntityHandlerManagerInterface $handler_manager * The parent entity handler manager. */ public function __construct( @@ -169,7 +169,7 @@ class SectionBreadcrumbBuilder implements BreadcrumbBuilderInterface { ]; } - // If parent is taxonomy_term, that's our context (handled via site_section). + // If parent is taxonomy_term, that's our context (handled via site_sections). if ($parent_entity_type === 'taxonomy_term') { return [ 'type' => 'taxonomy_term', diff --git a/src/Form/SiteStructureSettingsForm.php b/src/Form/StructuralPagesSettingsForm.php similarity index 89% rename from src/Form/SiteStructureSettingsForm.php rename to src/Form/StructuralPagesSettingsForm.php index ba04496..753582a 100644 --- a/src/Form/SiteStructureSettingsForm.php +++ b/src/Form/StructuralPagesSettingsForm.php @@ -2,20 +2,21 @@ declare(strict_types=1); -namespace Drupal\site_structure\Form; +namespace Drupal\structural_pages\Form; use Drupal\Core\Config\ConfigFactoryInterface; +use Drupal\Core\Config\TypedConfigManagerInterface; use Drupal\Core\Entity\EntityTypeBundleInfoInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Form\ConfigFormBase; use Drupal\Core\Form\FormStateInterface; -use Drupal\site_structure\ParentEntityHandler\ParentEntityHandlerManagerInterface; +use Drupal\structural_pages\ParentEntityHandler\ParentEntityHandlerManagerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** - * Configuration form for Site Structure module. + * Configuration form for Structural Pages module. */ -class SiteStructureSettingsForm extends ConfigFormBase { +class StructuralPagesSettingsForm extends ConfigFormBase { /** * The entity type manager. @@ -34,29 +35,32 @@ class SiteStructureSettingsForm extends ConfigFormBase { /** * The parent entity handler manager. * - * @var \Drupal\site_structure\ParentEntityHandler\ParentEntityHandlerManagerInterface + * @var \Drupal\structural_pages\ParentEntityHandler\ParentEntityHandlerManagerInterface */ protected ParentEntityHandlerManagerInterface $handlerManager; /** - * Constructs a SiteStructureSettingsForm object. + * Constructs a StructuralPagesSettingsForm object. * * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory * The factory for configuration objects. + * @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config_manager + * The typed config manager. * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager * The entity type manager. * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $entity_type_bundle_info * The entity type bundle info service. - * @param \Drupal\site_structure\ParentEntityHandler\ParentEntityHandlerManagerInterface $handler_manager + * @param \Drupal\structural_pages\ParentEntityHandler\ParentEntityHandlerManagerInterface $handler_manager * The parent entity handler manager. */ public function __construct( ConfigFactoryInterface $config_factory, + TypedConfigManagerInterface $typed_config_manager, EntityTypeManagerInterface $entity_type_manager, EntityTypeBundleInfoInterface $entity_type_bundle_info, ParentEntityHandlerManagerInterface $handler_manager, ) { - parent::__construct($config_factory); + parent::__construct($config_factory, $typed_config_manager); $this->entityTypeManager = $entity_type_manager; $this->entityTypeBundleInfo = $entity_type_bundle_info; $this->handlerManager = $handler_manager; @@ -68,6 +72,7 @@ class SiteStructureSettingsForm extends ConfigFormBase { public static function create(ContainerInterface $container): static { return new static( $container->get('config.factory'), + $container->get('config.typed'), $container->get('entity_type.manager'), $container->get('entity_type.bundle.info'), $container->get('plugin.manager.parent_entity_handler'), @@ -78,21 +83,21 @@ class SiteStructureSettingsForm extends ConfigFormBase { * {@inheritdoc} */ public function getFormId(): string { - return 'site_structure_settings_form'; + return 'structural_pages_settings_form'; } /** * {@inheritdoc} */ protected function getEditableConfigNames(): array { - return ['site_structure.settings']; + return ['structural_pages.settings']; } /** * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state): array { - $config = $this->config('site_structure.settings'); + $config = $this->config('structural_pages.settings'); $allowed_targets = $config->get('allowed_parent_targets') ?? []; // Build a keyed array for easier lookup. @@ -217,7 +222,7 @@ class SiteStructureSettingsForm extends ConfigFormBase { } } - $this->config('site_structure.settings') + $this->config('structural_pages.settings') ->set('allowed_parent_targets', $targets) ->save(); diff --git a/src/ParentEntityHandler/ParentEntityHandlerBase.php b/src/ParentEntityHandler/ParentEntityHandlerBase.php index defcc74..b22e820 100644 --- a/src/ParentEntityHandler/ParentEntityHandlerBase.php +++ b/src/ParentEntityHandler/ParentEntityHandlerBase.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\site_structure\ParentEntityHandler; +namespace Drupal\structural_pages\ParentEntityHandler; use Drupal\Core\Breadcrumb\Breadcrumb; use Drupal\Core\Entity\EntityInterface; diff --git a/src/ParentEntityHandler/ParentEntityHandlerInterface.php b/src/ParentEntityHandler/ParentEntityHandlerInterface.php index af612d3..57b9bdd 100644 --- a/src/ParentEntityHandler/ParentEntityHandlerInterface.php +++ b/src/ParentEntityHandler/ParentEntityHandlerInterface.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\site_structure\ParentEntityHandler; +namespace Drupal\structural_pages\ParentEntityHandler; use Drupal\Core\Breadcrumb\Breadcrumb; use Drupal\Core\Entity\EntityInterface; diff --git a/src/ParentEntityHandler/ParentEntityHandlerManager.php b/src/ParentEntityHandler/ParentEntityHandlerManager.php index 7d92626..1101748 100644 --- a/src/ParentEntityHandler/ParentEntityHandlerManager.php +++ b/src/ParentEntityHandler/ParentEntityHandlerManager.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\site_structure\ParentEntityHandler; +namespace Drupal\structural_pages\ParentEntityHandler; use Drupal\Core\Breadcrumb\Breadcrumb; use Drupal\Core\Cache\CacheBackendInterface; @@ -11,14 +11,14 @@ use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\Plugin\DefaultPluginManager; use Drupal\Core\Routing\RouteMatchInterface; -use Drupal\site_structure\Attribute\ParentEntityHandler; +use Drupal\structural_pages\Attribute\ParentEntityHandler; /** * Plugin manager for parent entity handlers. * - * @see \Drupal\site_structure\Attribute\ParentEntityHandler - * @see \Drupal\site_structure\ParentEntityHandler\ParentEntityHandlerInterface - * @see \Drupal\site_structure\ParentEntityHandler\ParentEntityHandlerBase + * @see \Drupal\structural_pages\Attribute\ParentEntityHandler + * @see \Drupal\structural_pages\ParentEntityHandler\ParentEntityHandlerInterface + * @see \Drupal\structural_pages\ParentEntityHandler\ParentEntityHandlerBase */ class ParentEntityHandlerManager extends DefaultPluginManager implements ParentEntityHandlerManagerInterface { @@ -32,7 +32,7 @@ class ParentEntityHandlerManager extends DefaultPluginManager implements ParentE /** * Static cache for handler instances. * - * @var \Drupal\site_structure\ParentEntityHandler\ParentEntityHandlerInterface[]|null + * @var \Drupal\structural_pages\ParentEntityHandler\ParentEntityHandlerInterface[]|null */ protected ?array $handlerInstances = NULL; diff --git a/src/ParentEntityHandler/ParentEntityHandlerManagerInterface.php b/src/ParentEntityHandler/ParentEntityHandlerManagerInterface.php index 3287024..3f9f5de 100644 --- a/src/ParentEntityHandler/ParentEntityHandlerManagerInterface.php +++ b/src/ParentEntityHandler/ParentEntityHandlerManagerInterface.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\site_structure\ParentEntityHandler; +namespace Drupal\structural_pages\ParentEntityHandler; use Drupal\Core\Breadcrumb\Breadcrumb; use Drupal\Core\Entity\EntityInterface; @@ -22,7 +22,7 @@ interface ParentEntityHandlerManagerInterface { * Handlers are available if their provider module is installed and the * entity type exists. * - * @return \Drupal\site_structure\ParentEntityHandler\ParentEntityHandlerInterface[] + * @return \Drupal\structural_pages\ParentEntityHandler\ParentEntityHandlerInterface[] * An array of available handler instances, keyed by plugin ID. */ public function getAvailableHandlers(): array; @@ -33,7 +33,7 @@ interface ParentEntityHandlerManagerInterface { * @param string $entity_type_id * The entity type ID. * - * @return \Drupal\site_structure\ParentEntityHandler\ParentEntityHandlerInterface|null + * @return \Drupal\structural_pages\ParentEntityHandler\ParentEntityHandlerInterface|null * The handler for the entity type, or NULL if none found. */ public function getHandlerForEntityType(string $entity_type_id): ?ParentEntityHandlerInterface; @@ -44,7 +44,7 @@ interface ParentEntityHandlerManagerInterface { * @param \Drupal\Core\Entity\EntityInterface $entity * The entity. * - * @return \Drupal\site_structure\ParentEntityHandler\ParentEntityHandlerInterface|null + * @return \Drupal\structural_pages\ParentEntityHandler\ParentEntityHandlerInterface|null * The handler for the entity, or NULL if none found. */ public function getHandlerForEntity(EntityInterface $entity): ?ParentEntityHandlerInterface; diff --git a/src/Plugin/Block/SiteStructureMenuBlock.php b/src/Plugin/Block/StructuralPagesMenuBlock.php similarity index 95% rename from src/Plugin/Block/SiteStructureMenuBlock.php rename to src/Plugin/Block/StructuralPagesMenuBlock.php index 85b8715..646fbbe 100644 --- a/src/Plugin/Block/SiteStructureMenuBlock.php +++ b/src/Plugin/Block/StructuralPagesMenuBlock.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Drupal\site_structure\Plugin\Block; +namespace Drupal\structural_pages\Plugin\Block; use Drupal\Core\Block\BlockBase; use Drupal\Core\Cache\Cache; @@ -12,22 +12,22 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\node\NodeInterface; -use Drupal\site_structure\ParentEntityHandler\ParentEntityHandlerManagerInterface; +use Drupal\structural_pages\ParentEntityHandler\ParentEntityHandlerManagerInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** * Provides a dynamic menu block based on site structure hierarchy. * * @Block( - * id = "site_structure_menu", - * admin_label = @Translation("Site Structure Menu"), - * category = @Translation("Site Structure"), + * id = "structural_pages_menu", + * admin_label = @Translation("Structural Pages Menu"), + * category = @Translation("Structural Pages"), * context_definitions = { * "node" = @ContextDefinition("entity:node", label = @Translation("Node"), required = FALSE), * } * ) */ -class SiteStructureMenuBlock extends BlockBase implements ContainerFactoryPluginInterface { +class StructuralPagesMenuBlock extends BlockBase implements ContainerFactoryPluginInterface { /** * Maximum depth to prevent infinite loops. @@ -51,7 +51,7 @@ class SiteStructureMenuBlock extends BlockBase implements ContainerFactoryPlugin /** * The parent entity handler manager. * - * @var \Drupal\site_structure\ParentEntityHandler\ParentEntityHandlerManagerInterface + * @var \Drupal\structural_pages\ParentEntityHandler\ParentEntityHandlerManagerInterface */ protected ParentEntityHandlerManagerInterface $handlerManager; @@ -175,7 +175,7 @@ class SiteStructureMenuBlock extends BlockBase implements ContainerFactoryPlugin } $build = [ - '#theme' => 'site_structure_menu', + '#theme' => 'structural_pages_menu', '#ancestor' => $ancestor, '#tree' => $tree, '#active_trail' => $active_trail, diff --git a/src/Plugin/ParentEntityHandler/NodeHandler.php b/src/Plugin/ParentEntityHandler/NodeHandler.php index 86ff857..4b6cef3 100644 --- a/src/Plugin/ParentEntityHandler/NodeHandler.php +++ b/src/Plugin/ParentEntityHandler/NodeHandler.php @@ -2,15 +2,15 @@ declare(strict_types=1); -namespace Drupal\site_structure\Plugin\ParentEntityHandler; +namespace Drupal\structural_pages\Plugin\ParentEntityHandler; use Drupal\Core\Breadcrumb\Breadcrumb; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\StringTranslation\TranslatableMarkup; use Drupal\node\NodeInterface; -use Drupal\site_structure\Attribute\ParentEntityHandler; -use Drupal\site_structure\ParentEntityHandler\ParentEntityHandlerBase; +use Drupal\structural_pages\Attribute\ParentEntityHandler; +use Drupal\structural_pages\ParentEntityHandler\ParentEntityHandlerBase; /** * Handler for node entities. diff --git a/src/Plugin/ParentEntityHandler/TaxonomyTermHandler.php b/src/Plugin/ParentEntityHandler/TaxonomyTermHandler.php index c2b344b..718d0fe 100644 --- a/src/Plugin/ParentEntityHandler/TaxonomyTermHandler.php +++ b/src/Plugin/ParentEntityHandler/TaxonomyTermHandler.php @@ -2,13 +2,13 @@ declare(strict_types=1); -namespace Drupal\site_structure\Plugin\ParentEntityHandler; +namespace Drupal\structural_pages\Plugin\ParentEntityHandler; use Drupal\Core\Breadcrumb\Breadcrumb; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\StringTranslation\TranslatableMarkup; -use Drupal\site_structure\Attribute\ParentEntityHandler; -use Drupal\site_structure\ParentEntityHandler\ParentEntityHandlerBase; +use Drupal\structural_pages\Attribute\ParentEntityHandler; +use Drupal\structural_pages\ParentEntityHandler\ParentEntityHandlerBase; use Drupal\taxonomy\TermInterface; /** @@ -20,7 +20,7 @@ use Drupal\taxonomy\TermInterface; entity_type_id: 'taxonomy_term', clears_site_section: FALSE, sort_field: 'name', - bundle_restrictions: ['site_section'], + bundle_restrictions: ['site_sections'], weight: 0, )] class TaxonomyTermHandler extends ParentEntityHandlerBase { @@ -69,8 +69,8 @@ class TaxonomyTermHandler extends ParentEntityHandlerBase { return NULL; } - // Only site_section terms can be site sections. - if ($entity->bundle() !== 'site_section') { + // Only site_sections terms can be site sections. + if ($entity->bundle() !== 'site_sections') { return NULL; } diff --git a/src/Plugin/ParentEntityHandler/UserHandler.php b/src/Plugin/ParentEntityHandler/UserHandler.php index d953380..ae42f76 100644 --- a/src/Plugin/ParentEntityHandler/UserHandler.php +++ b/src/Plugin/ParentEntityHandler/UserHandler.php @@ -2,13 +2,13 @@ declare(strict_types=1); -namespace Drupal\site_structure\Plugin\ParentEntityHandler; +namespace Drupal\structural_pages\Plugin\ParentEntityHandler; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\StringTranslation\TranslatableMarkup; -use Drupal\site_structure\Attribute\ParentEntityHandler; -use Drupal\site_structure\ParentEntityHandler\ParentEntityHandlerBase; +use Drupal\structural_pages\Attribute\ParentEntityHandler; +use Drupal\structural_pages\ParentEntityHandler\ParentEntityHandlerBase; use Drupal\user\UserInterface; /** diff --git a/site_structure.info.yml b/structural_pages.info.yml similarity index 85% rename from site_structure.info.yml rename to structural_pages.info.yml index 999cc3a..3077d22 100644 --- a/site_structure.info.yml +++ b/structural_pages.info.yml @@ -1,9 +1,9 @@ -name: 'Site Structure' +name: 'Structural Pages' type: module description: 'Implements hierarchical editorial structure and contextual navigation for institutional sites.' package: Custom core_version_requirement: ^11 -configure: site_structure.settings +configure: structural_pages.settings dependencies: - drupal:node - drupal:taxonomy diff --git a/site_structure.install b/structural_pages.install similarity index 68% rename from site_structure.install rename to structural_pages.install index 142cb8e..536d0c8 100644 --- a/site_structure.install +++ b/structural_pages.install @@ -2,7 +2,7 @@ /** * @file - * Install, update and uninstall functions for the Site Structure module. + * Install, update and uninstall functions for the Structural Pages module. */ declare(strict_types=1); @@ -12,19 +12,19 @@ use Drupal\taxonomy\Entity\Term; /** * Implements hook_install(). */ -function site_structure_install(): void { - // Create default terms for site_section vocabulary. - _site_structure_create_default_terms(); +function structural_pages_install(): void { + // Create default terms for site_sections vocabulary. + _structural_pages_create_default_terms(); // Display success message. - \Drupal::messenger()->addStatus(t('Site Structure module installed successfully. Configure Pathauto patterns at /admin/config/search/path/patterns')); + \Drupal::messenger()->addStatus(t('Structural Pages module installed successfully. Configure Pathauto patterns at /admin/config/search/path/patterns')); } /** - * Creates default terms for the site_section vocabulary. + * Creates default terms for the site_sections vocabulary. */ -function _site_structure_create_default_terms(): void { - $vocabulary = 'site_section'; +function _structural_pages_create_default_terms(): void { + $vocabulary = 'site_sections'; // Check if terms already exist (avoid duplication on reinstall). $existing = \Drupal::entityQuery('taxonomy_term') @@ -100,7 +100,7 @@ function _site_structure_create_default_terms(): void { /** * Implements hook_uninstall(). */ -function site_structure_uninstall(): void { +function structural_pages_uninstall(): void { $entity_type_manager = \Drupal::entityTypeManager(); // Remove nodes from module's content types. @@ -119,10 +119,10 @@ function site_structure_uninstall(): void { } } - // Remove terms from site_section vocabulary. + // Remove terms from site_sections vocabulary. $tids = \Drupal::entityQuery('taxonomy_term') ->accessCheck(FALSE) - ->condition('vid', 'site_section') + ->condition('vid', 'site_sections') ->execute(); if ($tids) { $terms = $entity_type_manager->getStorage('taxonomy_term')->loadMultiple($tids); @@ -136,7 +136,7 @@ function site_structure_uninstall(): void { // Pathauto patterns. 'pathauto.pattern.section_page', 'pathauto.pattern.content_page', - 'pathauto.pattern.site_section_term', + 'pathauto.pattern.site_sections_term', // Entity displays. 'core.entity_form_display.node.section_page.default', 'core.entity_view_display.node.section_page.default', @@ -155,7 +155,7 @@ function site_structure_uninstall(): void { 'node.type.section_page', 'node.type.content_page', // Vocabulary. - 'taxonomy.vocabulary.site_section', + 'taxonomy.vocabulary.site_sections', ]; $config_factory = \Drupal::configFactory(); @@ -166,36 +166,53 @@ function site_structure_uninstall(): void { } } - \Drupal::messenger()->addStatus(t('Site Structure module uninstalled successfully.')); + \Drupal::messenger()->addStatus(t('Structural Pages module uninstalled successfully.')); +} + +/** + * Update content_page content type name from "Guide Page" to "Content Page". + */ +function structural_pages_update_10001(): void { + $config = \Drupal::configFactory()->getEditable('node.type.content_page'); + if (!$config->isNew()) { + $current_name = $config->get('name'); + // Update if still using old name. + if ($current_name === 'Guide Page') { + $config->set('name', 'Content Page'); + $config->set('description', 'Pages with hierarchical parent-child structure for content organization.'); + $config->save(); + \Drupal::messenger()->addStatus(t('Updated content_page content type name to "Content Page".')); + } + } } /** * Implements hook_requirements(). */ -function site_structure_requirements(string $phase): array { +function structural_pages_requirements(string $phase): array { $requirements = []; if ($phase === 'runtime') { - // Check if site_section vocabulary has terms. + // Check if site_sections vocabulary has terms. $term_count = \Drupal::entityQuery('taxonomy_term') ->accessCheck(FALSE) - ->condition('vid', 'site_section') + ->condition('vid', 'site_sections') ->count() ->execute(); if ($term_count === 0) { - $requirements['site_structure_terms'] = [ - 'title' => t('Site Structure'), - 'value' => t('No terms in site_section vocabulary'), - 'description' => t('The Site Structure module requires terms in the "Site Section" vocabulary. Add terms.', [ - ':url' => '/admin/structure/taxonomy/manage/site_section/add', + $requirements['structural_pages_terms'] = [ + 'title' => t('Structural Pages'), + 'value' => t('No terms in site_sections vocabulary'), + 'description' => t('The Structural Pages module requires terms in the "Site Sections" vocabulary. Add terms.', [ + ':url' => '/admin/structure/taxonomy/manage/site_sections/add', ]), 'severity' => REQUIREMENT_WARNING, ]; } else { - $requirements['site_structure_terms'] = [ - 'title' => t('Site Structure'), + $requirements['structural_pages_terms'] = [ + 'title' => t('Structural Pages'), 'value' => t('@count terms configured', ['@count' => $term_count]), 'severity' => REQUIREMENT_OK, ]; diff --git a/site_structure.libraries.yml b/structural_pages.libraries.yml similarity index 54% rename from site_structure.libraries.yml rename to structural_pages.libraries.yml index 56b1cc6..07d4454 100644 --- a/site_structure.libraries.yml +++ b/structural_pages.libraries.yml @@ -2,4 +2,4 @@ menu: version: VERSION css: component: - css/site-structure-menu.css: {} + css/structural-pages-menu.css: {} diff --git a/site_structure.links.menu.yml b/structural_pages.links.menu.yml similarity index 55% rename from site_structure.links.menu.yml rename to structural_pages.links.menu.yml index 86e8a17..1494e8b 100644 --- a/site_structure.links.menu.yml +++ b/structural_pages.links.menu.yml @@ -1,6 +1,6 @@ -site_structure.settings: - title: 'Site Structure' +structural_pages.settings: + title: 'Structural Pages' description: 'Configure allowed parent entity types for content pages.' - route_name: site_structure.settings + route_name: structural_pages.settings parent: site_tools.admin_config weight: 0 diff --git a/site_structure.module b/structural_pages.module similarity index 85% rename from site_structure.module rename to structural_pages.module index 39ad5a5..3183f13 100644 --- a/site_structure.module +++ b/structural_pages.module @@ -2,7 +2,7 @@ /** * @file - * Primary module hooks for Site Structure module. + * Primary module hooks for Structural Pages module. */ declare(strict_types=1); @@ -18,7 +18,7 @@ use Drupal\user\UserInterface; * Inherits field_site_section from parent to content_page. * Validates circular reference in field_parent_page. */ -function site_structure_entity_presave(EntityInterface $entity): void { +function structural_pages_entity_presave(EntityInterface $entity): void { if (!$entity instanceof NodeInterface || $entity->bundle() !== 'content_page') { return; } @@ -42,7 +42,7 @@ function site_structure_entity_presave(EntityInterface $entity): void { // Circular reference validation (only for node parents). if ($parent_entity_type === 'node' && !$entity->isNew() && $parent_id) { - if (_site_structure_creates_circular_reference($entity->id(), $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); @@ -52,7 +52,7 @@ function site_structure_entity_presave(EntityInterface $entity): void { // Handle site section based on parent type. // Some entity types (like user and group) act as context containers themselves. - /** @var \Drupal\site_structure\ParentEntityHandler\ParentEntityHandlerManagerInterface $handler_manager */ + /** @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. @@ -61,7 +61,7 @@ function site_structure_entity_presave(EntityInterface $entity): void { } // Inherit field_site_section based on parent type (node or taxonomy_term). - $site_section_id = _site_structure_get_section_from_parent($parent_entity_type, $parent_id); + $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); } @@ -78,13 +78,13 @@ function site_structure_entity_presave(EntityInterface $entity): void { * @return int|string|null * The site section term ID, or NULL if not found. */ -function _site_structure_get_section_from_parent(string $parent_entity_type, int|string $parent_id): int|string|null { +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 site_section vocabulary. + // If parent is a taxonomy term, verify it's from site_sections vocabulary. $term = $entity_type_manager->getStorage('taxonomy_term')->load($parent_id); - if ($term instanceof TermInterface && $term->bundle() === 'site_section') { + if ($term instanceof TermInterface && $term->bundle() === 'site_sections') { return $term->id(); } return NULL; @@ -114,7 +114,7 @@ function _site_structure_get_section_from_parent(string $parent_entity_type, int * @return array|null * An array with 'type' and 'entity' keys, or NULL if no parent. */ -function _site_structure_get_parent_context(NodeInterface $node): ?array { +function _structural_pages_get_parent_context(NodeInterface $node): ?array { if (!$node->hasField('field_parent_page') || $node->get('field_parent_page')->isEmpty()) { return NULL; } @@ -156,7 +156,7 @@ function _site_structure_get_parent_context(NodeInterface $node): ?array { * @return bool * TRUE if it would create circular reference, FALSE otherwise. */ -function _site_structure_creates_circular_reference(int|string $node_id, int|string $parent_id): bool { +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'); @@ -196,9 +196,9 @@ function _site_structure_creates_circular_reference(int|string $node_id, int|str /** * Implements hook_theme(). */ -function site_structure_theme(): array { +function structural_pages_theme(): array { return [ - 'site_structure_menu' => [ + 'structural_pages_menu' => [ 'variables' => [ 'ancestor' => NULL, 'tree' => [], @@ -206,7 +206,7 @@ function site_structure_theme(): array { 'show_ancestor_title' => TRUE, ], ], - 'site_structure_menu_tree' => [ + 'structural_pages_menu_tree' => [ 'variables' => [ 'items' => [], 'active_trail' => [], @@ -219,12 +219,12 @@ function site_structure_theme(): array { /** * Implements hook_token_info(). */ -function site_structure_token_info(): array { +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).'), + 'description' => t('The hierarchical path of the site_sections taxonomy (e.g., undergraduate/courses).'), ]; $info['tokens']['term']['hierarchy-path'] = [ @@ -238,14 +238,14 @@ function site_structure_token_info(): array { /** * Implements hook_tokens(). */ -function site_structure_tokens(string $type, array $tokens, array $data, array $options, $bubbleable_metadata): array { +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] = _site_structure_get_section_path($node); + $replacements[$original] = _structural_pages_get_section_path($node); } } } @@ -254,7 +254,7 @@ function site_structure_tokens(string $type, array $tokens, array $data, array $ $term = $data['term']; foreach ($tokens as $name => $original) { if ($name === 'hierarchy-path') { - $replacements[$original] = _site_structure_get_term_hierarchy_path($term); + $replacements[$original] = _structural_pages_get_term_hierarchy_path($term); } } } @@ -271,7 +271,7 @@ function site_structure_tokens(string $type, array $tokens, array $data, array $ * @return string * The section path (e.g., "undergraduate/courses") or empty string. */ -function _site_structure_get_section_path(NodeInterface $node): string { +function _structural_pages_get_section_path(NodeInterface $node): string { if (!$node->hasField('field_site_section') || $node->get('field_site_section')->isEmpty()) { return ''; } @@ -306,7 +306,7 @@ function _site_structure_get_section_path(NodeInterface $node): string { * @return string * The hierarchical path (e.g., "institutional/news") or empty string. */ -function _site_structure_get_term_hierarchy_path(TermInterface $term): 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). diff --git a/structural_pages.routing.yml b/structural_pages.routing.yml new file mode 100644 index 0000000..ac938c1 --- /dev/null +++ b/structural_pages.routing.yml @@ -0,0 +1,7 @@ +structural_pages.settings: + path: '/admin/config/local-modules/structural-pages' + defaults: + _form: '\Drupal\structural_pages\Form\StructuralPagesSettingsForm' + _title: 'Structural Pages Settings' + requirements: + _permission: 'administer site configuration' diff --git a/site_structure.services.yml b/structural_pages.services.yml similarity index 63% rename from site_structure.services.yml rename to structural_pages.services.yml index 5301808..24c3b3a 100644 --- a/site_structure.services.yml +++ b/structural_pages.services.yml @@ -1,14 +1,14 @@ services: plugin.manager.parent_entity_handler: - class: Drupal\site_structure\ParentEntityHandler\ParentEntityHandlerManager + class: Drupal\structural_pages\ParentEntityHandler\ParentEntityHandlerManager arguments: - '@container.namespaces' - '@cache.discovery' - '@module_handler' - '@entity_type.manager' - site_structure.breadcrumb.section: - class: Drupal\site_structure\Breadcrumb\SectionBreadcrumbBuilder + structural_pages.breadcrumb.section: + class: Drupal\structural_pages\Breadcrumb\SectionBreadcrumbBuilder arguments: - '@entity_type.manager' - '@plugin.manager.parent_entity_handler' diff --git a/site_structure.tokens.inc b/structural_pages.tokens.inc similarity index 52% rename from site_structure.tokens.inc rename to structural_pages.tokens.inc index c2ad05e..be01ed6 100644 --- a/site_structure.tokens.inc +++ b/structural_pages.tokens.inc @@ -2,13 +2,13 @@ /** * @file - * Token integration for Site Structure module. + * Token integration for Structural Pages module. * * Este arquivo existe para compatibilidade, mas os tokens são definidos - * diretamente em site_structure.module via hook_token_info() e hook_tokens(). + * diretamente em structural_pages.module via hook_token_info() e hook_tokens(). */ declare(strict_types=1); -// Os tokens são implementados em site_structure.module. +// Os tokens são implementados em structural_pages.module. // Este arquivo pode ser usado para funções auxiliares adicionais se necessário. diff --git a/templates/site-structure-menu.html.twig b/templates/structural-pages-menu.html.twig similarity index 63% rename from templates/site-structure-menu.html.twig rename to templates/structural-pages-menu.html.twig index 286d090..4cf87b0 100644 --- a/templates/site-structure-menu.html.twig +++ b/templates/structural-pages-menu.html.twig @@ -1,7 +1,7 @@ {# /** * @file - * Template for the site structure menu block. + * Template for the structural pages menu block. * * Available variables: * - ancestor: The ancestor entity (term, user, or group). @@ -16,11 +16,11 @@ * - show_ancestor_title: Whether to show the ancestor title as header. */ #} -{{ attach_library('site_structure/menu') }} +{{ attach_library('structural_pages/menu') }} {% if tree is not empty %} -