Skip to content

Commit 9726001

Browse files
authored
feat: Make template component folder configurable (#285)
1 parent 56e16ee commit 9726001

File tree

11 files changed

+66
-12
lines changed

11 files changed

+66
-12
lines changed

djangocms_frontend/common/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
module = import_module(f"{__name__}.{settings.framework}.{module}", module)
2525
for cls in classes:
2626
globals()[cls] = getattr(module, cls)
27-
except ModuleNotFoundError:
27+
except ModuleNotFoundError: # pragma: no cover
2828
for cls in classes:
2929
globals()[cls] = type(cls, (object,), {})
3030

djangocms_frontend/component_pool.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from collections import defaultdict
2+
from collections.abc import Iterator
23
import importlib
34
import os
4-
from collections.abc import Iterator
55
import warnings
66

77
from django import forms
@@ -15,16 +15,16 @@
1515
from djangocms_frontend.component_base import CMSFrontendComponent
1616

1717

18-
def find_cms_component_templates() -> list[tuple[str, str]]:
18+
def find_cms_component_templates(subfolder: str) -> list[tuple[str, str]]:
1919
templates = []
2020
for app in apps.get_app_configs():
21-
app_template_dir = os.path.join(app.path, "templates", app.label, "cms_components")
21+
app_template_dir = os.path.join(app.path, "templates", app.label, subfolder)
2222
if os.path.exists(app_template_dir):
2323
for root, _, files in os.walk(app_template_dir):
2424
for file in files:
2525
if file.endswith(".html") or file.endswith(".htm"):
2626
relative_path = os.path.relpath(os.path.join(root, file), app_template_dir)
27-
templates.append((app.module.__name__, f"{app.label}/cms_components/{relative_path}"))
27+
templates.append((app.module.__name__, f"{app.label}/{subfolder}/{relative_path}"))
2828
return templates
2929

3030

@@ -43,7 +43,7 @@ class CMSAutoComponentDiscovery:
4343

4444
def __init__(self, register_to):
4545
self.default_field_context.update(settings.COMPONENT_FIELDS)
46-
templates = find_cms_component_templates()
46+
templates = find_cms_component_templates(settings.COMPONENT_FOLDER)
4747
auto_components = self.scan_templates_for_component_declaration(templates)
4848
for component in auto_components:
4949
register_to.register(component)

djangocms_frontend/settings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@
7777
FORM_OPTIONS = getattr(django_settings, "DJANGOCMS_FRONTEND_FORM_OPTIONS", {})
7878

7979
COMPONENT_FIELDS = getattr(django_settings, "DJANGOCMS_FRONTEND_COMPONENT_FIELDS", {})
80-
80+
COMPONENT_FOLDER = getattr(django_settings, "DJANGOCMS_FRONTEND_COMPONENT_FOLDER", "cms_components")
8181
framework = getattr(django_settings, "DJANGOCMS_FRONTEND_FRAMEWORK", "bootstrap5")
8282
theme = getattr(django_settings, "DJANGOCMS_FRONTEND_THEME", "djangocms_frontend")
8383

djangocms_frontend/templatetags/cms_component.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def _to_tuple_if_needed(value: str) -> str | tuple[str, str]:
6161
value (str): The string to be converted.
6262
6363
Returns:
64-
str | tuple[str, str]: A tuple containing the two parts of the string if it contains
64+
str | tuple[str, str]: A tuple containing the two parts of the string if it contains
6565
a delimiter, otherwise returns the original string.
6666
"""
6767
match = _TUPLE_RE.fullmatch(value)
@@ -82,7 +82,7 @@ def split(value: str, delimiter: str = "|") -> list[str | tuple[str, str]]:
8282
delimiter (str, optional): The delimiter to use for splitting the string. Defaults to "|".
8383
8484
Returns:
85-
list[str | tuple[str, str]: A list of substrings or 2-tuples obtained by splitting the
85+
list[str | tuple[str, str]: A list of substrings or 2-tuples obtained by splitting the
8686
input string using the delimiter.
8787
"""
8888
split_list = value.split(delimiter)

djangocms_frontend/templatetags/frontend.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ def render_tag(self, context, instance, attribute, **kwargs):
317317
instance = context.get("instance", None) # Use instance from context
318318

319319
if is_registering_component(context) and attribute:
320+
# Autodetect inline field and add it to the component
320321
update_component_properties(context, "frontend_editable_fields", attribute, append=True)
321322
elif is_inline_editing_active(context) and isinstance(instance, CMSPlugin) and instance.pk:
322323
# Only allow inline field to be rendered if inline editing is active and the instance is a CMSPlugin

docs/source/reference.rst

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,24 @@ in your project's ``settings.py``.
2828
]
2929

3030

31+
.. py:attribute:: settings.DJANGOCMS_FRONTEND_COMPONENT_FOLDER
32+
33+
Defaults to ``"cms_components"``
34+
35+
The subfolder where the component templates are discovered. This is used by the
36+
:ref:`template components <template_components>` to find the templates
37+
for the components.
38+
39+
The folder needs to be created in your app's ``templates/<app_name>/`` directory.
40+
If you want to use a different folder, set this to the folder name of your choice.
41+
42+
For example, if you want to use ``"my_components"``, add the following line to your project's settings::
43+
44+
DJANGOCMS_FRONTEND_COMPONENT_FOLDER = "my_components"
45+
46+
This causes djangocms-frontend to search for templates on the following paths: ``templates/<app_name>/my_components/``,
47+
where ``<app_name>`` is the name of any installed app.
48+
3149
.. py:attribute:: settings.DJANGOCMS_FRONTEND_COMPONENT_FIELDS
3250
3351
Defaults to ``{}``

docs/source/tutorial/template_components.rst

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,21 @@ Hero component
5252
==============
5353

5454
``djangocms-frontend`` allows developers to extend its functionality by creating
55-
template components**. In this tutorial, we will create an **Hero component**
55+
**template components**. In this tutorial, we will create an **Hero component**
5656
with the following fields:
5757

5858
- ``title``: A required text field.
5959
- ``slogan``: A required text area field.
6060
- ``hero_image``: A required image field.
6161

62-
This component will be stored in a template directory named ``<app_name>/cms_components``,
63-
as required for ``djangocms-frontend`` template components.
62+
This component will be stored in a template directory named ``<app_name>/cms_components``
63+
(or any subdirectory thereof).
64+
65+
.. note::
66+
You can change the location of your template components inside your template directory
67+
by setting the :attr:`DJANGOCMS_FRONTEND_COMPONENT_FOLDER` setting. The default is
68+
``cms_components``. If you change it, you need to adjust the directory structure accordingly.
69+
6470

6571
Directory Structure
6672
-------------------

tests/content/test_plugins.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,15 @@ def test_figure_plugin(self):
7979
response = self.client.get(self.request_url)
8080
self.assertEqual(response.status_code, 200)
8181
self.assertContains(response, '<figcaption class="figure-caption')
82+
83+
def test_safe_caption_tag(self):
84+
from djangocms_frontend.templatetags.frontend import safe_caption
85+
86+
# no paragraphs: unchanged
87+
self.assertEqual(safe_caption("hello world"), "hello world")
88+
89+
# single paragraph: stripped
90+
self.assertEqual(safe_caption("<p>hello world</p>"), "hello world")
91+
92+
# multiple paragraphs: kept as is
93+
self.assertEqual(safe_caption("<p>hello</p><p>world</p>"), "<p>hello</p><p>world</p>")

tests/test_app/templates/test_app/cms_components/ui/button.html

Whitespace-only changes.

tests/test_app/templates/test_app/my_components/test_component.htm

Whitespace-only changes.

0 commit comments

Comments
 (0)