Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions cms_theme/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,11 @@
class CmsThemeConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "cms_theme"

def ready(self) -> None:
from cms.plugin_pool import plugin_pool

code_plugin = plugin_pool.get_plugin("CodeBlockPlugin")
if code_plugin:
Comment on lines +11 to +12
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.

code_plugin.change_form_template = "code_block/admin/code_block.html"

38 changes: 38 additions & 0 deletions cms_theme/cms_components.py
Original file line number Diff line number Diff line change
Expand Up @@ -925,3 +925,41 @@ def clean(self):
),
}
)


@components.register
class CodeBlock(CMSFrontendComponent):
"""Code card component to render code snippets with syntax highlighting"""
class Media:
js = (
"admin/vendor/ace/ace.js"
if "djangocms_static_ace" in settings.INSTALLED_APPS
else "https://cdnjs.cloudflare.com/ajax/libs/ace/1.43.3/ace.js",
)

class Meta:
name = _("Code Block")
render_template = "code_block/code_block.html"
change_form_template = "code_block/admin/code_block.html"
allow_children = True
child_classes = []
mixins = ["Background", "Spacing", "Attributes"]
frontend_editable_fields = ("heading",)

heading = forms.CharField(
label=_("Heading"),
required=False,
help_text=_("Heading for the code block."),
)
dark_mode = forms.BooleanField(
label=_("Dark mode"),
required=False,
initial=False,
help_text=_("Enable dark mode for code block."),
)
code_content = forms.CharField(
label=_("Code"),
initial="",
required=True,
widget=forms.widgets.Textarea(attrs={"class": "js-ckeditor-use-selected-text"}),
)
2 changes: 1 addition & 1 deletion cms_theme/templates/cms_theme/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
<script src="{% static 'js/logo_carousel.js' %}"></script>
<script src="{% static 'js/mobile_slider.js' %}"></script>
<script src="{% static 'js/quote_slider.js' %}"></script>
<script>hljs.highlightAll();</script>
<script>document.addEventListener('DOMContentLoaded', function() { hljs.highlightAll(); });</script>
{% endblock end_js %}
</body>
</html>
46 changes: 46 additions & 0 deletions cms_theme/templates/code_block/admin/code_block.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{% extends "djangocms_frontend/admin/base.html" %}

{% block object-tools %}
{{ block.super }}{% spaceless %}
<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.

var settings = textarea.data();
var div = django.jQuery('<div>', {
position: 'absolute',
width: '100%',
style: 'font-size: 14px',
height: textarea.height() * 4,
'class': textarea.attr('class'),
}).insertBefore(textarea);

// init editor with settings
var editor = ace.edit(div[0]);
var darkMode;
darkMode = window.parent.CMS.API.Helpers.getColorScheme() === 'dark';
if (darkMode) {
editor.setTheme('ace/theme/tomorrow_night');
} else {
editor.setTheme('ace/theme/xcode');
}
editor.getSession().setValue(textarea.val());
editor.getSession().setMode('ace/mode/python');
editor.setOptions({
fontSize: '14px',
cursorStyle: 'smooth'
});
editor.renderer.setScrollMargin(5, 5);

// send data back to textarea when submitting
textarea.closest('form').submit(function () {
textarea.val(editor.getSession().getValue());
});

// immediate update while typing inside CKEditor
editor.getSession().on('change', function () {
textarea.val(editor.getSession().getValue());
});
});
</script>{% endspaceless %}
{% endblock %}
11 changes: 11 additions & 0 deletions cms_theme/templates/code_block/code_block.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{% load i18n cms_tags frontend %}
<div class="mb-5 card {% if instance.dark_mode %}bg-dark{% else %}bg-light{% endif %}">
{% if instance.heading %}
<div class="card-header bg-primary">
<p><span class="overline">{% inline_field instance "heading" %}</span></p>
</div>
{% endif %}
<div class="card-body{% if instance.dark_mode %} text-light{% endif %}">
<pre{{ instance.get_attributes }}><code>{{ instance.code_content }}</code></pre>
</div>
</div>