Skip to content

Add content reference blocks (ContentChooserBlock + ContentCardBlock)#1713

Open
mwvolo wants to merge 6 commits into
mainfrom
content-reference-block
Open

Add content reference blocks (ContentChooserBlock + ContentCardBlock)#1713
mwvolo wants to merge 6 commits into
mainfrom
content-reference-block

Conversation

@mwvolo

@mwvolo mwvolo commented Jun 25, 2026

Copy link
Copy Markdown
Member

What

Adds a native, reusable way for CMS editors to link a card (and other blocks) to another page on the site — a content-reference primitive plus a card that uses it. This is the CMS half of the work behind the structured card set (relates to CORE-2404, the blog card).

New blocks (pages/custom_blocks.py)

  • ContentChooserBlock — a PageChooserBlock restricted to an allowlist (CONTENT_REFERENCE_TYPES = books.Book, news.NewsArticle). Emits a lean canonical reference: {id, type, slug, url, title}. URL resolves via .specific (same pattern as LinkBlock), so book links come out as the real frontend route (/details/books/<slug>), not the tree path.
  • ContentCardBlock — a StructBlock wrapping the reference with optional title / image / excerpt overrides.

content_card is registered in BASE_CONTENT_BLOCKS (with the generated StreamField migration 0181_alter_rootpage_body).

Why this shape: reference-and-hydrate

The CMS emits a lean reference + overrides only and embeds no page display data. The frontend (flex-pages renderer) hydrates fresh display data from the pages API by reference. This mirrors the pattern in flex-pages PR #14 (the os books block), avoids stale embedded snapshots, and means the existing BookBlock / get_book_data path is untouched — this change is purely additive.

Unset-override contract: unset title/image/excerpt serialize as null (not ""/{}) so the frontend override ?? hydrated merge falls back to the page's own values correctly.

Scope

  • Pages only (no snippets / external URLs — those stay in LinkBlock).
  • The flex-pages renderer half (hydrator + ContentCardBlock component) is a separate follow-on in the flex-pages repo.

Testing

6 tests in pages/tests/test_content_reference.py (canonical reference shape, the null-override contract for all three fields, allowlist enforcement, block registration). All passing.

Ran 6 tests in 0.172s — OK

🤖 Generated with Claude Code

mwvolo and others added 3 commits June 25, 2026 11:09
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Unset overrides (title/image/excerpt) serialize as null so the frontend
override ?? hydrated merge falls back to the referenced page's values.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds the content_card block to the shared content block list and the
generated StreamField migration (0181_alter_rootpage_body).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 25, 2026 16:56

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a reusable “content reference” primitive for StreamField content in OpenStax CMS, allowing editors to create card-like blocks that reference other site pages (initially Books and News Articles) while emitting a lean API payload intended for frontend hydration.

Changes:

  • Introduces ContentChooserBlock (allowlisted PageChooserBlock) and ContentCardBlock (reference + optional overrides) in pages/custom_blocks.py.
  • Registers the new content_card block in BASE_CONTENT_BLOCKS and applies the generated StreamField migration.
  • Adds a focused test suite validating canonical reference shape, allowlist behavior, and override serialization expectations.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
pages/custom_blocks.py Adds the new content-reference blocks and API serialization logic for references/cards.
pages/models/constants.py Registers content_card into the base StreamField block set.
pages/migrations/0181_alter_rootpage_body.py Updates RootPage.body StreamField to include content_card.
pages/tests/test_content_reference.py Adds tests covering canonical reference output, null-override contract, and registration.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pages/custom_blocks.py
Comment thread pages/tests/test_content_reference.py
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@mwvolo mwvolo added the wip label Jun 25, 2026
mwvolo and others added 2 commits June 26, 2026 15:44
# Conflicts:
#	pages/migrations/0181_alter_rootpage_body.py
Resolves the duplicate 0181_alter_rootpage_body conflict with main
(#1714). Keeps main's 0181 (big_number color) and renumbers the
content_card StreamField change to 0182, depending on 0181.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants