Skip to content

Commit 107b7cc

Browse files
authored
fix: Validate template component names (#333)
1 parent ac8a4d5 commit 107b7cc

File tree

3 files changed

+45
-3
lines changed

3 files changed

+45
-3
lines changed

djangocms_frontend/templatetags/cms_component.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ class based on the implate it is part of. The component class is generated
3030
if "_cms_components" in context:
3131
if len(args) != 1: # pragma: no cover
3232
raise ValueError("The cms_component tag requires exactly one positional argument: the component name.")
33+
if not isinstance(args[0], str):
34+
raise ValueError("The component name must be a string.")
35+
if not args[0].isidentifier():
36+
raise ValueError("The component name must be a valid Python identifier.")
3337
context["_cms_components"]["cms_component"].append((args, kwargs))
3438
return ""
3539

docs/source/tutorial/template_components.rst

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ Simplified Component Creation with Templates
1010

1111
.. versionadded:: 2.1
1212

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

1717

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

107+
.. note::
108+
The component name (the first argument to ``{% cms_component %}``) must be a valid Python identifier.
109+
This means it should start with a letter or underscore, followed by letters, digits, or underscores,
110+
and cannot contain spaces or special characters like hyphens.
111+
107112
Only one ``{% cms_component %}`` tag is allowed per template file.
108113

109114
The first part is the declarative part of the template:

tests/test_autocomponent.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,39 @@ def test_multiple_cms_component_tags_error(self):
107107
with self.assertRaises(TemplateSyntaxError):
108108
Template(invalid_template)
109109

110+
def test_cms_component_invalid_identifier(self):
111+
# Test that cms_component tag raises ValueError for invalid identifiers
112+
from django.template import Context
113+
from djangocms_frontend.templatetags.cms_component import cms_component
114+
115+
context = Context({"_cms_components": {"cms_component": []}})
116+
117+
# Valid identifier should work
118+
cms_component(context, "valid_name")
119+
self.assertEqual(len(context["_cms_components"]["cms_component"]), 1)
120+
121+
# Non-string identifiers should raise ValueError
122+
with self.assertRaises(ValueError) as cm:
123+
cms_component(context, 123)
124+
self.assertIn("component name must be a string.", str(cm.exception))
125+
126+
# Invalid identifiers should raise ValueError
127+
with self.assertRaises(ValueError) as cm:
128+
cms_component(context, "invalid-name")
129+
self.assertIn("valid Python identifier", str(cm.exception))
130+
131+
with self.assertRaises(ValueError) as cm:
132+
cms_component(context, "123invalid")
133+
self.assertIn("valid Python identifier", str(cm.exception))
134+
135+
with self.assertRaises(ValueError) as cm:
136+
cms_component(context, "invalid name")
137+
self.assertIn("valid Python identifier", str(cm.exception))
138+
139+
with self.assertRaises(ValueError) as cm:
140+
cms_component(context, "")
141+
self.assertIn("valid Python identifier", str(cm.exception))
142+
110143
def test_component_folder_selection(self):
111144
from djangocms_frontend.component_pool import find_cms_component_templates
112145

0 commit comments

Comments
 (0)