# 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 |