Initial commit: Site Structure module for Drupal

Drupal module that provides hierarchical site structure management
with support for sections, categories, and content items. Includes
path aliases with tokens, breadcrumb integration, and admin interface.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-03 19:55:00 -03:00
commit 8a42a6f1c1
31 changed files with 2633 additions and 0 deletions

345
docs/DESIGN.md Normal file
View File

@@ -0,0 +1,345 @@
# Site Structure - 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.
## Problem
In Drupal, content is added in a flat manner (non-hierarchical), and menus are traditionally used to organize content into hierarchical structures. This creates user experience problems because content editors typically do not have permission to administer menus.
## Solution
The module implements two complementary structures:
1. **Taxonomic Structure** (primary): Organization based on hierarchical taxonomy vocabulary
2. **Parent-Child Structure** (subsidiary): Direct hierarchy between pages for specific cases, supporting multiple entity types as parents
---
## Conceptual Model
```
┌─────────────────────────────────────────────────────────────────────┐
│ PRIMARY STRUCTURE: site_section Taxonomy │
│ │
│ - Governs the overall organization of site information │
│ - Defines breadcrumbs and URLs (via Pathauto) │
│ - Example: Undergraduate > Courses > Software Engineering │
│ - Applied to content type: section_page │
├─────────────────────────────────────────────────────────────────────┤
│ SUBSIDIARY STRUCTURE: Parent-Child (field_parent_page) │
│ │
│ - For specific cases: manuals, guides, sequential documentation │
│ - Provides contextual navigation (Child pages View) │
│ - Automatically inherits site section from parent (node/taxonomy) │
│ - For user/group parents, context is the entity itself │
│ - Applied to content type: content_page │
│ - Supports multiple parent types: content_page, section_page, │
│ taxonomy terms, users, groups (configurable) │
├─────────────────────────────────────────────────────────────────────┤
│ page (core Drupal) │
│ │
│ - Remains untouched for generic use │
└─────────────────────────────────────────────────────────────────────┘
```
---
## Components
### 1. Taxonomy Vocabulary: `site_section`
| Property | Value |
|----------|-------|
| Machine name | `site_section` |
| Name | Site Section |
| Hierarchical | Yes |
| Description | Main hierarchical structure for site organization |
**Usage**: This vocabulary defines site sections. Terms can be nested to create hierarchies (e.g., "Undergraduate" > "Courses" > "Engineering").
### 2. Content Type: `section_page`
| Property | Value |
|----------|-------|
| Machine name | `section_page` |
| Name | Section Page |
| Description | Pages organized by site section |
**Fields**:
| Field | Type | Required | Cardinality | Description |
|-------|------|----------|-------------|-------------|
| `title` | String | Yes | 1 | Page title (core) |
| `body` | Text (formatted, long, with summary) | No | 1 | Page content |
| `field_site_section` | Entity reference (taxonomy) | Yes | 1 | Site section |
### 3. Content Type: `content_page`
| Property | Value |
|----------|-------|
| Machine name | `content_page` |
| Name | Content Page |
| Description | Pages with hierarchical parent-child structure |
**Fields**:
| Field | Type | Required | Cardinality | Description |
|-------|------|----------|-------------|-------------|
| `title` | String | Yes | 1 | Page title (core) |
| `body` | Text (formatted, long, with summary) | No | 1 | Page content |
| `field_parent_page` | Dynamic entity reference | No | 1 | Parent entity (configurable types) |
| `field_site_section` | Entity reference (taxonomy) | Conditional | 1 | Site section (inherited or manual) |
**Rules for `field_site_section` in content_page**:
- If `field_parent_page` points to a `taxonomy_term`: uses the term itself as site section
- If `field_parent_page` points to a `node`: inherits `field_site_section` from parent
- If `field_parent_page` is empty (root page): must be filled manually
### 4. Dynamic Parent Reference
The `field_parent_page` uses the Dynamic Entity Reference module to support multiple entity types as parents. This is configurable via the admin interface.
**Default allowed targets**:
- `node:content_page` - Other content pages
- `node:section_page` - Section pages
- `taxonomy_term:site_section` - Site section terms
- `user:user` - User accounts
- `group:*` - All group types (requires Group module)
**Context behavior by parent type**:
- **Node/Taxonomy**: Content pages inherit `field_site_section` from the parent
- **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`
### 5. Inheritance Logic
Implemented via `hook_entity_presave()`:
```
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):
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
ELSE:
Validate that field_site_section was filled manually
```
**Additional validations**:
- Prevent self-reference (page cannot be parent of itself)
- Prevent circularity (A → B → C → A) - only for node parents
### 6. Breadcrumb Builder
Custom service that overrides Drupal's default breadcrumb for `section_page` and `content_page`.
**Logic**:
1. Determine the root parent context by traversing the parent chain
2. Build breadcrumb based on context type:
- **User context**: Home > User Name > Parent Nodes > Current Page
- **Group context**: Home > Group Name > Parent Nodes > Current Page
- **Taxonomy context**: Home > Term Hierarchy > Parent Nodes > Current Page
3. For content_page, also add node parents to the breadcrumb
**Examples**:
- Page "Chapter 1" has parent "User Guide" which is a section_page in "Documentation"
- Breadcrumb: Home > Documentation > User Guide > Chapter 1
- Page "My Notes" has parent user "John Doe"
- Breadcrumb: Home > John Doe > My Notes
- Page "Meeting Minutes" has parent group "Research Team"
- Breadcrumb: Home > Research Team > Meeting Minutes
### 7. Pathauto Tokens
Custom token for hierarchical URL generation.
| Token | Description | Example |
|-------|-------------|---------|
| `[node:site-section-path]` | Path based on taxonomy hierarchy | `undergraduate/courses` |
**Suggested URL pattern**: `[node:site-section-path]/[node:title]`
**Result**: `/undergraduate/courses/software-engineering`
### 8. View: `child_pages`
View that lists child pages of the current `content_page`.
| Property | Value |
|----------|-------|
| Display | Block |
| Filter | `field_parent_page` = Current node ID (contextual) |
| Sort | Title (A-Z) or weight (if implemented) |
| Usage | Sidebar navigation in guides/manuals |
### 9. Settings Form
Configuration form at `/admin/config/local-modules/site-structure` 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
---
## File Structure
```
site_structure/
├── site_structure.info.yml # Metadata and dependencies
├── site_structure.module # Hooks (presave, tokens)
├── 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
├── config/
│ ├── install/
│ │ ├── site_structure.settings.yml # Default settings
│ │ ├── taxonomy.vocabulary.site_section.yml
│ │ │
│ │ ├── node.type.section_page.yml
│ │ ├── field.storage.node.field_site_section.yml
│ │ ├── field.field.node.section_page.field_site_section.yml
│ │ ├── core.entity_form_display.node.section_page.default.yml
│ │ ├── core.entity_view_display.node.section_page.default.yml
│ │ │
│ │ ├── node.type.content_page.yml
│ │ ├── field.storage.node.field_parent_page.yml # dynamic_entity_reference
│ │ ├── field.field.node.content_page.field_parent_page.yml
│ │ ├── field.field.node.content_page.field_site_section.yml
│ │ ├── core.entity_form_display.node.content_page.default.yml
│ │ ├── core.entity_view_display.node.content_page.default.yml
│ │ │
│ │ ├── pathauto.pattern.section_page.yml
│ │ ├── pathauto.pattern.content_page.yml
│ │ ├── pathauto.pattern.site_section_term.yml
│ │ │
│ │ └── views.view.child_pages.yml
│ │
│ └── schema/
│ └── site_structure.schema.yml # Config schema
├── translations/
│ └── pt-br.po # Portuguese (Brazil) translation
├── src/
│ ├── Breadcrumb/
│ │ └── SectionBreadcrumbBuilder.php
│ │
│ └── Form/
│ └── SiteStructureSettingsForm.php
└── docs/
└── DESIGN.md # This document
```
---
## Dependencies
### Core Modules (Drupal 11)
- `node`
- `taxonomy`
- `views`
- `field`
- `text`
- `user`
### Contrib Modules
- `token` - For custom tokens
- `pathauto` - For automatic URL generation
- `dynamic_entity_reference` - For multi-type parent references
### Optional Modules
- `group` - For group entity support as parent type
---
## Editor Workflow
### Creating a Section Page (section_page)
1. Navigate to Content > Add content > Section Page
2. Fill in title and body
3. Select the appropriate site section
4. Save
5. URL and breadcrumb are generated automatically
### Creating a Content Page (content_page)
1. **Create root page (anchored to taxonomy term)**:
- Add content > Content Page
- In "Parent Page", select a site_section term
- Site section is set automatically from the term
- Save
2. **Create root page (anchored to section_page)**:
- Add content > Content Page
- In "Parent Page", select a section_page
- Site section is inherited from the section_page
- Save
3. **Create child pages**:
- Add content > Content Page
- In "Parent Page", select another content_page
- Site section is inherited automatically
- Save
4. The child pages View appears automatically on parent pages
---
## Administration
### Configuring Allowed Parent Types
1. Navigate to `/admin/config/local-modules/site-structure`
2. Select which content types (nodes) can be used as parents
3. Select which taxonomy vocabularies can be used as parents
4. Save configuration
5. Field configuration is updated automatically
---
## Group Integration
The module supports the Group module for content organization:
- Content pages can have groups as parent entities
- When a content_page has a group as parent, the context is the group itself
- Breadcrumbs show: Home > Group Name > Parent Nodes > Current Page
- `field_site_section` is cleared for group-parented content (context is the group)
- Per-section permissions can be implemented via Groups
**Configuration**: Enable group types in the settings form at `/admin/config/local-modules/site-structure`
---
## Installation Verification
1. `drush en site_structure -y`
2. Verify vocabulary at `/admin/structure/taxonomy`
3. Verify content types at `/admin/structure/types`
4. Create test hierarchical terms
5. Create `section_page` and verify breadcrumb/URL
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`
---
## Changelog
| Version | Date | Description |
|---------|------|-------------|
| 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 |