Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
4 changes: 4 additions & 0 deletions djangocms_frontend/templatetags/cms_component.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ class based on the implate it is part of. The component class is generated
if "_cms_components" in context:
if len(args) != 1: # pragma: no cover
raise ValueError("The cms_component tag requires exactly one positional argument: the component name.")
if not isinstance(args[0], str):
raise ValueError("The component name must be a string.")
if not args[0].isidentifier():
raise ValueError("The component name must be a valid Python identifier.")
context["_cms_components"]["cms_component"].append((args, kwargs))
return ""

Expand Down
9 changes: 7 additions & 2 deletions docs/source/tutorial/template_components.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ Simplified Component Creation with Templates

.. versionadded:: 2.1

**Template components** are the easiest approach to creating or porting your own custom
**Template components** are the easiest approach to creating or porting your own custom
frontend components, allowing you to define custom components **using django templates,
without needing to write any Python code**.
without needing to write any Python code**.


Example Hero Template Component
Expand Down Expand Up @@ -104,6 +104,11 @@ by django CMS to identify the plugin later. The ``name`` parameter is used to di
component in the CMS admin interface. Internally the command declares a ``CMSFrontendComponent``
class. All named arguments are added to the component's Meta class.

.. note::
The component name (the first argument to ``{% cms_component %}``) must be a valid Python identifier.
This means it should start with a letter or underscore, followed by letters, digits, or underscores,
and cannot contain spaces or special characters like hyphens.

Only one ``{% cms_component %}`` tag is allowed per template file.

The first part is the declarative part of the template:
Expand Down
28 changes: 28 additions & 0 deletions tests/test_autocomponent.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,34 @@ def test_multiple_cms_component_tags_error(self):
with self.assertRaises(TemplateSyntaxError):
Template(invalid_template)

def test_cms_component_invalid_identifier(self):
# Test that cms_component tag raises ValueError for invalid identifiers
from django.template import Context
from djangocms_frontend.templatetags.cms_component import cms_component

context = Context({"_cms_components": {"cms_component": []}})

# Valid identifier should work
cms_component(context, "valid_name")
self.assertEqual(len(context["_cms_components"]["cms_component"]), 1)

# Invalid identifiers should raise ValueError
with self.assertRaises(ValueError) as cm:
cms_component(context, "invalid-name")
self.assertIn("valid Python identifier", str(cm.exception))

with self.assertRaises(ValueError) as cm:
cms_component(context, "123invalid")
self.assertIn("valid Python identifier", str(cm.exception))

with self.assertRaises(ValueError) as cm:
cms_component(context, "invalid name")
self.assertIn("valid Python identifier", str(cm.exception))

with self.assertRaises(ValueError) as cm:
cms_component(context, "")
self.assertIn("valid Python identifier", str(cm.exception))

def test_component_folder_selection(self):
from djangocms_frontend.component_pool import find_cms_component_templates

Expand Down