Structure Content Editor Runtime and API

This developer note documents the BricksMembers Structure Content Editor runtime: route rendering, selected-post payloads, per-post save ownership, native Content Fields handling, compatible custom field handling, and the guardrails that keep content editing separate from hierarchy mutation.

Scope

The Structure Content Editor is the in-admin content editing mode at admin.php?page=brm_structure_{structure_id}&mode=content. It lets structure editors select a post from a navigation-only tree, edit that post’s title/body/settings, and save without entering Structure view Edit Mode.

It does not own structure CRUD, parent assignment, ordering, staged commit, duplication, trash, restore, delete, or projection rebuild orchestration.

Runtime Owners

  • src/Admin/Pages/Routes/StructuresPage.php routes the structure page and selects the content-editor template for mode=content.
  • src/Admin/Pages/Templates/contract/structure-content-editor.php renders the route-local shell, initial JSON state, tabs, navigation tree wrapper, center content area, custom-field section, and right settings panel targets.
  • src/Structures/ContentEditor/StructureContentEditorDataProvider.php builds the page data and selected-post payload.
  • src/Structures/Rendering/StructureAdminRenderer.php renders the simplified content-navigation tree and the row action that opens the selected post in Content mode.
  • assets/admin/js/structures/structures-content-editor.js owns client-side selection, dirty state, panel rendering, save payload assembly, left navigation resizing, custom-field initialization, and media picker interaction.
  • src/Structures/Ajax/StructureContentEditorAjax.php owns selected-item reads and compatible custom-field saves.
  • src/Modules/ContentFields/ContentFieldValueService.php owns native Content Fields value writes when the Content Editor saves the content_fields payload.
  • src/Structures/Ajax/StructureQuickEditAjax.php remains the normal per-post field write endpoint for title, slug, content, author, media, access, module settings, taxonomies, quiz, downloads, submissions, video policy, and drip override.
  • src/Admin/AdminWysiwyg.php and assets/admin/js/admin-wysiwyg.js own the reusable inline classic editor.

Route Contract

  • admin.php?page=brm_structure_{structure_id} opens the hierarchy/structure editing view.
  • admin.php?page=brm_structure_{structure_id}&mode=content opens the Content Editor and selects the first post when no post_id is provided.
  • admin.php?page=brm_structure_{structure_id}&mode=content&post_id=7414 opens the Content Editor with a specific selected post.
  • brm_structure_content_editor_enabled can disable the content editor for a specific structure when page data is built.

Selected Item Payload

StructureContentEditorDataProvider::build_item_payload() returns the selected post payload used by both initial page state and brm_get_structure_content_item.

  • Core post fields: post_id, post_type, post_title, post_name, post_content, post_status, post_author, and assignable authors.
  • Editor routing: body_mode (classic, gutenberg, or bricks), edit_wordpress_url, edit_bricks_url, and preview_url.
  • Media and video: authored video value, saved video provider, connected providers, duration, video image, and featured image.
  • Access and module data: required levels, level options, parent/children flags, enrollment rule, effective enrollment requirement, submissions, assigned quiz, assigned downloads, video watch policy, and drip override.
  • Taxonomy data: hierarchical taxonomy term IDs plus non-hierarchical tag names.
  • Compatible custom fields: acf.active and rendered field-group HTML when an ACF-compatible runtime is available.
  • Native Content Fields: content_fields.active, matching scalar field definitions, current values, and save eligibility when the Content Fields module is active and a field group matches the selected post.

AJAX Endpoints

brm_get_structure_content_item

Reads one selected-post payload. The handler verifies the shared structure editor nonce, requires edit_posts, and also checks edit_post for the requested post ID.

brm_save_structure_content_acf

Saves compatible custom-field values. The handler requires the same nonce and capabilities as the read endpoint. It only calls update_field() for field keys discovered from field groups assigned to the selected post, including nested subfields and flexible-content layouts.

This endpoint is for ACF-compatible runtime fields only. It must not save BRM-owned Content Fields.

brm_save_structure_content_fields

Saves BricksMembers-owned Content Fields for the selected post. The handler requires the same nonce and capabilities as the read endpoint, then delegates the submitted content_fields array to ContentFieldValueService::save_values_for_post(). Required-field validation and value sanitization stay in the Content Fields value owner.

Normal post and module settings continue through brm_quick_edit_post_fields. Do not add another write endpoint for the same fields.

Content Body Modes

  • classic – the inline BricksMembers WYSIWYG loads and saves post_content.
  • gutenberg – the content editor shows a WordPress edit fallback. The inline classic editor is not used for block content.
  • bricks – the content editor shows the Bricks edit fallback. The inline classic editor is not used for Bricks body data.

Security and Escaping

  • Structure content editor AJAX uses StructureAjaxVerifier::verify_editor_request(), which enforces the BRM AJAX nonce, edit_posts, POST-only requests, and the shared AJAX rate-limit context.
  • Each selected-post request additionally checks Security::current_user_can( 'edit_post', $post_id ).
  • The template serializes initial state with wp_json_encode() and JSON hex flags.
  • Navigation tree HTML is rendered server-side and passed through a merged wp_kses_allowed_html( 'post' ) and admin-icon allowlist before output.
  • Client-rendered admin strings must come from localized PHP data. Do not add hardcoded user-facing English strings in the JavaScript.

Implementation Invariants

  • The Content Editor tree is navigation-only.
  • The right settings sidebar stays fixed at the route CSS variable width. Left-navigation resizing changes only the left and center columns.
  • Compatible custom fields render below the body in the center column, not inside the settings sidebar.
  • Native Content Fields render in their own Content Fields section below the body. Keep the Content Fields save path separate from the ACF-compatible save path.
  • The visible custom-field label is generic. Do not hardcode ACF, SCF, or another provider name into the admin label.
  • Module-gated panels should only render when the relevant module and post type support are active.
  • Do not route internal code through brm_* public API wrappers for this surface.
Get BricksMembers

Start Building Your Membership Site Today

Create, sell, and manage your content without limits. BricksMembers gives you everything you need to build membership and LMS sites with Bricks Builder.

Lifetime updates & bug fixes • Premium support • 0% transaction fees • 60-day money-back guarantee