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

View File

@@ -0,0 +1,95 @@
langcode: en
status: true
dependencies:
config:
- field.field.node.content_page.body
- field.field.node.content_page.field_parent_page
- field.field.node.content_page.field_site_section
- node.type.content_page
module:
- dynamic_entity_reference
- path
- text
id: node.content_page.default
targetEntityType: node
bundle: content_page
mode: default
content:
body:
type: text_textarea_with_summary
weight: 2
region: content
settings:
rows: 9
summary_rows: 3
placeholder: ''
show_summary: false
third_party_settings: { }
created:
type: datetime_timestamp
weight: 10
region: content
settings: { }
third_party_settings: { }
field_parent_page:
type: dynamic_entity_reference_default
weight: 0
region: content
settings:
match_operator: CONTAINS
match_limit: 10
size: 60
placeholder: ''
third_party_settings: { }
field_site_section:
type: options_select
weight: 1
region: content
settings: { }
third_party_settings: { }
path:
type: path
weight: 30
region: content
settings: { }
third_party_settings: { }
promote:
type: boolean_checkbox
weight: 15
region: content
settings:
display_label: true
third_party_settings: { }
status:
type: boolean_checkbox
weight: 120
region: content
settings:
display_label: true
third_party_settings: { }
sticky:
type: boolean_checkbox
weight: 16
region: content
settings:
display_label: true
third_party_settings: { }
title:
type: string_textfield
weight: -5
region: content
settings:
size: 60
placeholder: ''
third_party_settings: { }
uid:
type: entity_reference_autocomplete
weight: 5
region: content
settings:
match_operator: CONTAINS
match_limit: 10
size: 60
placeholder: ''
third_party_settings: { }
hidden: { }

View File

@@ -0,0 +1,83 @@
langcode: en
status: true
dependencies:
config:
- field.field.node.section_page.body
- field.field.node.section_page.field_site_section
- node.type.section_page
module:
- path
- text
id: node.section_page.default
targetEntityType: node
bundle: section_page
mode: default
content:
body:
type: text_textarea_with_summary
weight: 1
region: content
settings:
rows: 9
summary_rows: 3
placeholder: ''
show_summary: false
third_party_settings: { }
created:
type: datetime_timestamp
weight: 10
region: content
settings: { }
third_party_settings: { }
field_site_section:
type: options_select
weight: 0
region: content
settings: { }
third_party_settings: { }
path:
type: path
weight: 30
region: content
settings: { }
third_party_settings: { }
promote:
type: boolean_checkbox
weight: 15
region: content
settings:
display_label: true
third_party_settings: { }
status:
type: boolean_checkbox
weight: 120
region: content
settings:
display_label: true
third_party_settings: { }
sticky:
type: boolean_checkbox
weight: 16
region: content
settings:
display_label: true
third_party_settings: { }
title:
type: string_textfield
weight: -5
region: content
settings:
size: 60
placeholder: ''
third_party_settings: { }
uid:
type: entity_reference_autocomplete
weight: 5
region: content
settings:
match_operator: CONTAINS
match_limit: 10
size: 60
placeholder: ''
third_party_settings: { }
hidden: { }

View File

@@ -0,0 +1,30 @@
langcode: en
status: true
dependencies:
config:
- field.field.node.content_page.body
- field.field.node.content_page.field_parent_page
- field.field.node.content_page.field_site_section
- node.type.content_page
module:
- text
id: node.content_page.default
targetEntityType: node
bundle: content_page
mode: default
content:
body:
type: text_default
label: hidden
settings: { }
third_party_settings: { }
weight: 0
region: content
links:
settings: { }
third_party_settings: { }
weight: 100
region: content
hidden:
field_parent_page: true
field_site_section: true

View File

@@ -0,0 +1,28 @@
langcode: en
status: true
dependencies:
config:
- field.field.node.section_page.body
- field.field.node.section_page.field_site_section
- node.type.section_page
module:
- text
id: node.section_page.default
targetEntityType: node
bundle: section_page
mode: default
content:
body:
type: text_default
label: hidden
settings: { }
third_party_settings: { }
weight: 0
region: content
links:
settings: { }
third_party_settings: { }
weight: 100
region: content
hidden:
field_site_section: true

View File

@@ -0,0 +1,22 @@
langcode: en
status: true
dependencies:
config:
- field.storage.node.body
- node.type.content_page
module:
- text
id: node.content_page.body
field_name: body
entity_type: node
bundle: content_page
label: Body
description: ''
required: false
translatable: true
default_value: { }
default_value_callback: ''
settings:
display_summary: true
required_summary: false
field_type: text_with_summary

View File

@@ -0,0 +1,44 @@
langcode: en
status: true
dependencies:
config:
- field.storage.node.field_parent_page
- node.type.content_page
- node.type.section_page
- taxonomy.vocabulary.site_section
module:
- dynamic_entity_reference
id: node.content_page.field_parent_page
field_name: field_parent_page
entity_type: node
bundle: content_page
label: 'Parent Page'
description: 'Select the parent entity. The site section will be inherited automatically based on the parent type.'
required: false
translatable: false
default_value: { }
default_value_callback: ''
settings:
entity_type_ids:
- node
- taxonomy_term
node:
handler: 'default:node'
handler_settings:
target_bundles:
content_page: content_page
section_page: section_page
sort:
field: title
direction: asc
auto_create: false
taxonomy_term:
handler: 'default:taxonomy_term'
handler_settings:
target_bundles:
site_section: site_section
sort:
field: name
direction: asc
auto_create: false
field_type: dynamic_entity_reference

View File

@@ -0,0 +1,29 @@
langcode: en
status: true
dependencies:
config:
- field.storage.node.field_site_section
- node.type.content_page
- taxonomy.vocabulary.site_section
module:
- taxonomy
id: node.content_page.field_site_section
field_name: field_site_section
entity_type: node
bundle: content_page
label: 'Site Section'
description: 'For root pages, select the section. For child pages, this field is filled automatically.'
required: false
translatable: false
default_value: { }
default_value_callback: ''
settings:
handler: 'default:taxonomy_term'
handler_settings:
target_bundles:
site_section: site_section
sort:
field: name
direction: asc
auto_create: false
field_type: entity_reference

View File

@@ -0,0 +1,22 @@
langcode: en
status: true
dependencies:
config:
- field.storage.node.body
- node.type.section_page
module:
- text
id: node.section_page.body
field_name: body
entity_type: node
bundle: section_page
label: Body
description: ''
required: false
translatable: true
default_value: { }
default_value_callback: ''
settings:
display_summary: true
required_summary: false
field_type: text_with_summary

View File

@@ -0,0 +1,29 @@
langcode: en
status: true
dependencies:
config:
- field.storage.node.field_site_section
- node.type.section_page
- taxonomy.vocabulary.site_section
module:
- taxonomy
id: node.section_page.field_site_section
field_name: field_site_section
entity_type: node
bundle: section_page
label: 'Site Section'
description: 'Select the site section this page belongs to.'
required: true
translatable: false
default_value: { }
default_value_callback: ''
settings:
handler: 'default:taxonomy_term'
handler_settings:
target_bundles:
site_section: site_section
sort:
field: name
direction: asc
auto_create: false
field_type: entity_reference

View File

@@ -0,0 +1,18 @@
langcode: en
status: true
dependencies:
module:
- dynamic_entity_reference
- node
id: node.field_parent_page
field_name: field_parent_page
entity_type: node
type: dynamic_entity_reference
settings: { }
module: dynamic_entity_reference
locked: false
cardinality: 1
translatable: true
indexes: { }
persist_with_no_fields: false
custom_storage: false

View File

@@ -0,0 +1,19 @@
langcode: en
status: true
dependencies:
module:
- node
- taxonomy
id: node.field_site_section
field_name: field_site_section
entity_type: node
type: entity_reference
settings:
target_type: taxonomy_term
module: core
locked: false
cardinality: 1
translatable: true
indexes: { }
persist_with_no_fields: false
custom_storage: false

View File

@@ -0,0 +1,13 @@
langcode: en
status: true
dependencies: { }
third_party_settings:
pathauto:
enabled: true
name: 'Content Page'
type: content_page
description: 'Pages with hierarchical parent-child structure for content organization.'
help: 'For root pages, fill in the Site Section manually. For child pages, select the parent page and the section will be inherited automatically.'
new_revision: true
preview_mode: 1
display_submitted: true

View File

@@ -0,0 +1,13 @@
langcode: en
status: true
dependencies: { }
third_party_settings:
pathauto:
enabled: true
name: 'Section Page'
type: section_page
description: 'Pages organized by site section, with URLs and breadcrumbs based on taxonomy.'
help: ''
new_revision: true
preview_mode: 1
display_submitted: true

View File

@@ -0,0 +1,20 @@
langcode: en
status: true
dependencies:
module:
- node
- site_structure
id: content_page
label: 'Content Page'
type: 'canonical_entities:node'
pattern: '[node:site-section-path]/[node:title]'
selection_logic: and
selection_criteria:
c4fb8cde-f2d5-5bf5-0e99-303706509174:
id: entity_bundle:node
negate: false
context_mapping:
node: node
bundles:
content_page: content_page
weight: 0

View File

@@ -0,0 +1,20 @@
langcode: en
status: true
dependencies:
module:
- node
- site_structure
id: section_page
label: 'Section Page'
type: 'canonical_entities:node'
pattern: '[node:site-section-path]/[node:title]'
selection_logic: and
selection_criteria:
b3eb7bda-e1c4-4ae4-9d88-292695498063:
id: entity_bundle:node
negate: false
context_mapping:
node: node
bundles:
section_page: section_page
weight: 0

View File

@@ -0,0 +1,20 @@
langcode: en
status: true
dependencies:
module:
- site_structure
- taxonomy
id: site_section_term
label: 'Site Section Term'
type: 'canonical_entities:taxonomy_term'
pattern: '[term:hierarchy-path]'
selection_logic: and
selection_criteria:
d5fc9eaf-f3e6-6cg6-1f00-414817610285:
id: entity_bundle:taxonomy_term
negate: false
context_mapping:
taxonomy_term: taxonomy_term
bundles:
site_section: site_section
weight: 0

View File

@@ -0,0 +1,11 @@
allowed_parent_targets:
- entity_type: node
bundle: content_page
- entity_type: node
bundle: section_page
- entity_type: taxonomy_term
bundle: site_section
- entity_type: user
bundle: user
- entity_type: group
bundle: '*'

View File

@@ -0,0 +1,7 @@
langcode: en
status: true
dependencies: { }
name: 'Site Section'
vid: site_section
description: 'Main hierarchical structure for site organization.'
weight: 0

View File

@@ -0,0 +1,252 @@
langcode: en
status: true
dependencies:
config:
- node.type.content_page
module:
- node
- user
id: child_pages
label: 'Child Pages'
module: views
description: 'Lists the child pages of a content_page.'
tag: ''
base_table: node_field_data
base_field: nid
display:
default:
id: default
display_title: Default
display_plugin: default
position: 0
display_options:
title: 'Child Pages'
fields:
title:
id: title
table: node_field_data
field: title
relationship: none
group_type: group
admin_label: ''
entity_type: node
entity_field: title
plugin_id: field
label: ''
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: false
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: value
type: string
settings:
link_to_entity: true
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
pager:
type: none
options:
offset: 0
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
options: { }
empty:
area_text_custom:
id: area_text_custom
table: views
field: area_text_custom
relationship: none
group_type: group
admin_label: ''
plugin_id: text_custom
empty: true
content: 'No child pages found.'
tokenize: false
sorts:
created:
id: created
table: node_field_data
field: created
relationship: none
group_type: group
admin_label: ''
entity_type: node
entity_field: created
plugin_id: date
order: ASC
expose:
label: ''
field_identifier: ''
exposed: false
granularity: second
arguments:
field_parent_page_target_id:
id: field_parent_page_target_id
table: node__field_parent_page
field: field_parent_page_target_id
relationship: none
group_type: group
admin_label: ''
plugin_id: numeric
default_action: default
exception:
value: all
title_enable: false
title: All
title_enable: false
title: ''
default_argument_type: node
default_argument_options: { }
summary_options:
base_path: ''
count: true
override: false
items_per_page: 25
summary:
sort_order: asc
number_of_records: 0
format: default_summary
specify_validation: true
validate:
type: 'entity:node'
fail: 'not found'
validate_options:
bundles:
content_page: content_page
access: true
operation: view
multiple: 0
break_phrase: false
not: false
filters:
status:
id: status
table: node_field_data
field: status
entity_type: node
entity_field: status
plugin_id: boolean
value: '1'
group: 1
expose:
operator: ''
type:
id: type
table: node_field_data
field: type
entity_type: node
entity_field: type
plugin_id: bundle
value:
content_page: content_page
group: 1
style:
type: default
options:
grouping: { }
row_class: ''
default_row_class: true
row:
type: fields
options:
default_field_elements: true
inline: { }
separator: ''
hide_empty: false
query:
type: views_query
options:
query_comment: ''
disable_sql_rewrite: false
distinct: false
replica: false
query_tags: { }
relationships: { }
header: { }
footer: { }
display_extenders: { }
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- 'user.node_grants:view'
- user.permissions
tags: { }
block_child_pages:
id: block_child_pages
display_title: 'Block: Child Pages'
display_plugin: block
position: 1
display_options:
display_extenders: { }
block_description: 'Child Pages'
block_category: 'Site Structure'
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- 'user.node_grants:view'
- user.permissions
tags: { }

View File

@@ -0,0 +1,16 @@
site_structure.settings:
type: config_object
label: 'Site Structure settings'
mapping:
allowed_parent_targets:
type: sequence
label: 'Allowed parent targets'
sequence:
type: mapping
mapping:
entity_type:
type: string
label: 'Entity type'
bundle:
type: string
label: 'Bundle'