Skip to content

feat: Add customer code block#131

Open
fsbraun wants to merge 1 commit intomainfrom
feat/code-block
Open

feat: Add customer code block#131
fsbraun wants to merge 1 commit intomainfrom
feat/code-block

Conversation

@fsbraun
Copy link
Member

@fsbraun fsbraun commented Mar 12, 2026

Summary by Sourcery

Introduce a reusable CMS code block component with syntax-highlighted editing and rendering, and adjust global syntax highlighting initialization.

New Features:

  • Add a CodeBlock CMS frontend component with heading, dark mode, and code content fields for displaying syntax-highlighted snippets.
  • Provide dedicated frontend and admin templates for rendering and editing code blocks, including an Ace-based editor in the admin.

Enhancements:

  • Initialize highlight.js after DOMContentLoaded to ensure code snippets are highlighted reliably.
  • Configure the CodeBlock plugin’s change form template at app startup via the Django app config.

Fixes #118

@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Mar 12, 2026

Reviewer's Guide

Adds a new CMS CodeBlock component with ACE-powered admin editing and a styled frontend template, updates the app config to override the plugin change form template, and adjusts highlight.js initialization timing for safer DOM usage.

Sequence diagram for CodeBlock admin editing with Ace editor

sequenceDiagram
    actor Editor
    participant DjangoAdmin as Django_admin
    participant CodeBlockPlugin as CodeBlock_plugin
    participant Browser as Browser_page
    participant AceEditor as Ace_editor

    Editor->>DjangoAdmin: Open CodeBlock change form
    DjangoAdmin->>CodeBlockPlugin: Resolve change_form_template
    CodeBlockPlugin-->>DjangoAdmin: code_block/admin/code_block.html
    DjangoAdmin-->>Browser: Render admin form with textarea and Ace init JS

    Browser->>Browser: Hide textarea and insert div before it
    Browser->>AceEditor: ace.edit(div)
    AceEditor->>AceEditor: Initialize theme based on parent CMS color scheme
    AceEditor->>AceEditor: Set mode ace/mode/python and options
    AceEditor->>Browser: Keep textarea value in sync on change

    Editor->>Browser: Submit form
    Browser->>Browser: Copy AceEditor session value into textarea
    Browser-->>DjangoAdmin: POST form with code_content
    DjangoAdmin-->>Editor: Save confirmation and redirect
Loading

Sequence diagram for frontend syntax highlighting timing change

sequenceDiagram
    participant Browser as Browser_page
    participant HLJS as highlight_js

    Browser->>Browser: DOMContentLoaded event fires
    Browser->>HLJS: hljs.highlightAll()
    HLJS-->>Browser: Highlight all code elements

    Note over Browser,HLJS: Initialization now waits for DOMContentLoaded instead of running immediately
Loading

Class diagram for new CodeBlock CMS component

classDiagram
    direction TB

    class CMSFrontendComponent {
    }

    class CodeBlock {
        +CharField heading
        +BooleanField dark_mode
        +CharField code_content
    }

    class CodeBlock_Media {
        +tuple js
    }

    class CodeBlock_Meta {
        +CharField name
        +CharField render_template
        +CharField change_form_template
        +Boolean allow_children
        +list child_classes
        +list mixins
        +tuple frontend_editable_fields
    }

    CMSFrontendComponent <|-- CodeBlock
    CodeBlock *-- CodeBlock_Media
    CodeBlock *-- CodeBlock_Meta
Loading

File-Level Changes

Change Details Files
Introduce a new CMS CodeBlock component for rendering code snippets with optional dark mode styling.
  • Register a new CMSFrontendComponent subclass named CodeBlock with appropriate Meta configuration for templates, children, mixins, and frontend-editable fields.
  • Add form fields for heading, dark_mode toggle, and required code_content using a textarea widget.
  • Configure conditional loading of ace.js from either a local static app or a CDN in the component's Media class.
cms_theme/cms_components.py
Hook into Django app initialization to adjust the CodeBlock plugin’s admin change form template.
  • Override AppConfig.ready to fetch CodeBlockPlugin from django CMS plugin_pool.
  • If the plugin exists, set its change_form_template to the custom code_block admin template.
cms_theme/apps.py
Ensure highlight.js runs after DOMContentLoaded instead of immediately.
  • Wrap hljs.highlightAll() in a DOMContentLoaded event listener in the base template end_js block.
cms_theme/templates/cms_theme/base.html
Add a custom admin change form integrating ACE editor for code editing in the CodeBlock component.
  • Extend djangocms_frontend admin base template and inject JavaScript to replace the code textarea with an ACE editor instance.
  • Hide the original textarea, mirror its value into the ACE editor, and keep them synchronized on change and on form submit.
  • Configure ACE editor appearance, theme selection based on parent CMS color scheme, and Python syntax mode.
cms_theme/templates/code_block/admin/code_block.html
Add the frontend template to render the CodeBlock component with Bootstrap card styling and dark mode support.
  • Render an optional heading using inline_field inside a card header with primary background.
  • Render the code_content inside a
     block, applying instance attributes and dark-mode dependent classes for background and text color.
cms_theme/templates/code_block/code_block.html

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • The Ace admin template currently hides and replaces all textarea elements on the page; it would be safer to target only the specific code field (e.g., by id or a dedicated CSS class) to avoid interfering with other form fields.
  • The Ace editor mode is hardcoded to ace/mode/python; if this component is intended for general code snippets, consider adding a language field or configuration so the syntax mode can be selected rather than fixed.
  • In CodeBlock.code_content, the js-ckeditor-use-selected-text class suggests integration with CKEditor, which may conflict with the Ace setup; consider removing or replacing this with a class specific to the code editor behavior.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The Ace admin template currently hides and replaces all `textarea` elements on the page; it would be safer to target only the specific code field (e.g., by `id` or a dedicated CSS class) to avoid interfering with other form fields.
- The Ace editor mode is hardcoded to `ace/mode/python`; if this component is intended for general code snippets, consider adding a language field or configuration so the syntax mode can be selected rather than fixed.
- In `CodeBlock.code_content`, the `js-ckeditor-use-selected-text` class suggests integration with CKEditor, which may conflict with the Ace setup; consider removing or replacing this with a class specific to the code editor behavior.

## Individual Comments

### Comment 1
<location path="cms_theme/templates/code_block/admin/code_block.html" line_range="8" />
<code_context>
+    <script>
+        django.jQuery(function () {
+            // ace editor cannot be attached directly to a textarea
+            var textarea = django.jQuery('textarea').css('display', 'none');
+            var settings = textarea.data();
+            var div = django.jQuery('<div>', {
</code_context>
<issue_to_address>
**issue (bug_risk):** The Ace editor setup targets all textareas, which can cause issues if the form has multiple textarea fields.

Since this binds Ace to every textarea, it can interfere with other fields now or in the future. Please scope the selector to the specific `code_content` field (e.g., by name, id, or a dedicated class) so other textareas are not hidden or synced unintentionally.
</issue_to_address>

### Comment 2
<location path="cms_theme/apps.py" line_range="11-12" />
<code_context>
+    def ready(self) -> None:
+        from cms.plugin_pool import plugin_pool
+
+        code_plugin = plugin_pool.get_plugin("CodeBlockPlugin")
+        if code_plugin:
+            code_plugin.change_form_template = "code_block/admin/code_block.html"
+            
</code_context>
<issue_to_address>
**issue (bug_risk):** The `ready()` hook may run before the plugin is registered, causing this customization to be silently skipped.

Because plugin registration timing can vary, `get_plugin("CodeBlockPlugin")` may return `None` here and the template override will never take effect. To avoid this, either ensure the module that registers the plugin is imported in `ready()` (e.g., import the app’s `cms_plugins` there), or move this customization alongside the plugin definition so it’s applied at registration time.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

<script>
django.jQuery(function () {
// ace editor cannot be attached directly to a textarea
var textarea = django.jQuery('textarea').css('display', 'none');
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (bug_risk): The Ace editor setup targets all textareas, which can cause issues if the form has multiple textarea fields.

Since this binds Ace to every textarea, it can interfere with other fields now or in the future. Please scope the selector to the specific code_content field (e.g., by name, id, or a dedicated class) so other textareas are not hidden or synced unintentionally.

Comment on lines +11 to +12
code_plugin = plugin_pool.get_plugin("CodeBlockPlugin")
if code_plugin:
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (bug_risk): The ready() hook may run before the plugin is registered, causing this customization to be silently skipped.

Because plugin registration timing can vary, get_plugin("CodeBlockPlugin") may return None here and the template override will never take effect. To avoid this, either ensure the module that registers the plugin is imported in ready() (e.g., import the app’s cms_plugins there), or move this customization alongside the plugin definition so it’s applied at registration time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Code block needs own component

1 participant