django-camomilla-cms 6.0.0b17__py2.py3-none-any.whl → 6.0.0b18__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
camomilla/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = "6.0.0-beta.17"
1
+ __version__ = "6.0.0-beta.18"
2
2
 
3
3
 
4
4
  def get_core_apps():
camomilla/settings.py CHANGED
@@ -87,6 +87,11 @@ API_TRANSLATION_ACCESSOR = pointed_getter(
87
87
  django_settings, "CAMOMILLA.API.TRANSLATION_ACCESSOR", "translations"
88
88
  )
89
89
 
90
+ REGISTERED_TEMPLATES_APPS = pointed_getter(
91
+ django_settings,
92
+ "CAMOMILLA.RENDER.REGISTERED_TEMPLATES_APPS", None
93
+ )
94
+
90
95
  DEBUG = pointed_getter(django_settings, "CAMOMILLA.DEBUG", django_settings.DEBUG)
91
96
 
92
97
  # camomilla settings example
@@ -104,6 +109,7 @@ DEBUG = pointed_getter(django_settings, "CAMOMILLA.DEBUG", django_settings.DEBUG
104
109
  # "AUTO_CREATE_HOMEPAGE": True,
105
110
  # "ARTICLE": {"DEFAULT_TEMPLATE": "", "INJECT_CONTEXT": None },
106
111
  # "PAGE": {"DEFAULT_TEMPLATE": "", "INJECT_CONTEXT": None }
112
+ # "REGISTERED_TEMPLATE_APPS": []
107
113
  # },
108
114
  # "STRUCTURED_FIELD": {
109
115
  # "CACHE_ENABLED": True
@@ -1,5 +1,7 @@
1
1
  {% load menus %}
2
2
  {% load i18n %}
3
+ {% load model_extras %}
4
+
3
5
  {% get_current_language as current_lang %}
4
6
 
5
7
 
@@ -9,9 +11,6 @@
9
11
  <title>Camomilla CMS</title>
10
12
  <style>
11
13
  html,
12
- body {
13
- height: 100%;
14
- }
15
14
 
16
15
  body {
17
16
  background-color: #f7fafc;
@@ -49,6 +48,45 @@
49
48
  font-size: 18px;
50
49
  font-style: italic;
51
50
  }
51
+ .accordion {
52
+ margin: 2rem auto;
53
+ border: 1px solid #000;
54
+ font-family: monospace;
55
+ width: 800px;
56
+ max-width: 800px;
57
+ }
58
+
59
+ .accordion-header {
60
+ display: flex;
61
+ justify-content: space-between;
62
+ align-items: center;
63
+ padding: 0.75rem 1rem;
64
+ cursor: pointer;
65
+ user-select: none;
66
+ }
67
+
68
+ .accordion-icon {
69
+ width: 1rem;
70
+ height: 1rem;
71
+ transition: transform 0.3s ease;
72
+ }
73
+
74
+ .accordion-toggle:checked + .accordion-header .accordion-icon {
75
+ transform: rotate(180deg);
76
+ }
77
+
78
+ .accordion-body {
79
+ max-height: 0;
80
+ overflow: hidden;
81
+ transition: max-height 0.3s ease;
82
+ }
83
+
84
+ .accordion-toggle:checked + .accordion-header + .accordion-body {
85
+ max-height: 30vh;
86
+ padding: 1rem;
87
+ border-top: 1px solid #000;
88
+ overflow-y: auto;
89
+ }
52
90
  </style>
53
91
  </head>
54
92
 
@@ -104,11 +142,29 @@
104
142
  - <b>Module:</b> {{page_model.module}} <br>
105
143
  - <b>Identifier:</b> {{page.pk}} <br>
106
144
  - <b>Permalink:</b> {{page.permalink}} <br>
107
- - <b>Language:</b> {{current_lang}} <br>
145
+ - <b>Language:</b> {{current_lang}} <br>
108
146
  </code>
147
+
109
148
  {% endblock %}
149
+
110
150
  {% block content %}
111
151
  {% endblock %}
152
+
153
+ <div class="accordion">
154
+ <input type="checkbox" id="accordion-toggle" class="accordion-toggle" hidden>
155
+ <label for="accordion-toggle" class="accordion-header">
156
+ <span>Page data</span>
157
+ <svg class="accordion-icon" viewBox="0 0 24 24">
158
+ <path d="M6 9l6 6 6-6" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round"/>
159
+ </svg>
160
+ </label>
161
+ <div class="accordion-body">
162
+ <pre>{{ page|to_pretty_dict|safe }}</pre>
163
+ </div>
164
+ </div>
165
+
166
+ </div>
167
+
112
168
  </body>
113
169
 
114
170
  </html>
@@ -0,0 +1,73 @@
1
+ from django import template
2
+ from django.utils.safestring import mark_safe
3
+ from django.forms.models import model_to_dict
4
+ from structured.pydantic.models import BaseModel
5
+ import json
6
+ import re
7
+ import html
8
+ from datetime import datetime
9
+
10
+ register = template.Library()
11
+
12
+
13
+ def custom_json_serializer(obj):
14
+ """Serialize custom objects to JSON."""
15
+ if isinstance(obj, datetime):
16
+ return obj.isoformat()
17
+ raise TypeError(f"Type {type(obj)} not serializable")
18
+
19
+
20
+ def pretty_dict(data, indent_level=0):
21
+ """
22
+ Recursive function to format a dictionary into a pretty string.
23
+ Args:
24
+ data (dict): The dictionary to format.
25
+ indent_level (int): The current indentation level.
26
+ Returns:
27
+ str: A pretty-printed string representation of the dictionary.
28
+ """
29
+ indent = " " * indent_level
30
+ result = []
31
+
32
+ for key, value in data.items():
33
+
34
+ if isinstance(value, dict):
35
+ result.append(f"{indent}'{key}': {{")
36
+ result.append(pretty_dict(value, indent_level + 1))
37
+ result.append(f"{indent}}},")
38
+
39
+ elif isinstance(value, list):
40
+ result.append(f"{indent}'{key}': [")
41
+ for item in value:
42
+ if isinstance(item, dict):
43
+ result.append(pretty_dict(item, indent_level + 1))
44
+ else:
45
+ result.append(f"{indent} {json.dumps(item, default=custom_json_serializer)},")
46
+ result.append(f"{indent}],")
47
+
48
+ else:
49
+ result.append(f"{indent}'{key}': {json.dumps(value, default=custom_json_serializer)},")
50
+
51
+ return "\n".join(result).rstrip(',')
52
+
53
+
54
+ @register.filter
55
+ def to_pretty_dict(instance):
56
+ data = model_to_dict(instance)
57
+
58
+ for key, value in data.items():
59
+ if isinstance(value, BaseModel):
60
+ data[key] = value.model_dump()
61
+
62
+ formatted_dict = "{\n" + pretty_dict(data, indent_level=1) + "\n}"
63
+
64
+ escaped = html.escape(formatted_dict)
65
+
66
+ # This to highlight the keys in the JSON
67
+ highlighted = re.sub(
68
+ r"(&#x27;)([^&#]+?)(&#x27;):",
69
+ r"<span style='color:#df3079'>'\2'</span>:",
70
+ escaped
71
+ )
72
+
73
+ return mark_safe(f"<pre>{highlighted}</pre>")
@@ -1 +1 @@
1
- __version__ = "6.0.0-beta.17"
1
+ __version__ = "6.0.0-beta.18"
camomilla/theme/apps.py CHANGED
@@ -34,4 +34,5 @@ class CamomillaThemeConfig(AppConfig):
34
34
  "django_jsonform",
35
35
  "admin_interface",
36
36
  "colorfield",
37
+ "structured",
37
38
  )
@@ -3,18 +3,31 @@ from typing import Sequence
3
3
 
4
4
  from django import template as django_template
5
5
  from os.path import relpath
6
+ from camomilla.settings import REGISTERED_TEMPLATES_APPS
6
7
 
7
8
 
8
9
  def get_all_templates_files() -> Sequence[str]:
9
- dirs = []
10
- for engine in django_template.loader.engines.all():
11
- # Exclude pip installed site package template dirs
12
- dirs.extend(
13
- x
14
- for x in engine.template_dirs
15
- if "site-packages" not in str(x) or "camomilla" in str(x)
16
- )
17
10
  files = []
18
- for dir in dirs:
19
- files.extend(relpath(x, dir) for x in Path(dir).glob("**/*.html") if x)
11
+
12
+ for engine in django_template.loader.engines.all():
13
+
14
+ if REGISTERED_TEMPLATES_APPS:
15
+ dirs = [
16
+ d for d in engine.template_dirs
17
+ if any(app in str(d) for app in REGISTERED_TEMPLATES_APPS)
18
+ ]
19
+ else:
20
+ # Exclude pip installed site package template dirs
21
+ dirs = [
22
+ d for d in engine.template_dirs
23
+ if "site-packages" not in str(d) or "camomilla" in str(d)
24
+ ]
25
+
26
+ for d in dirs:
27
+ base = Path(d)
28
+ files.extend(
29
+ relpath(f, d)
30
+ for f in base.rglob("*.html")
31
+ )
32
+
20
33
  return files
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-camomilla-cms
3
- Version: 6.0.0b17
3
+ Version: 6.0.0b18
4
4
  Summary: Django powered cms
5
5
  Author-email: Lotrèk <dimmitutto@lotrek.it>
6
6
  License: MIT
@@ -1,4 +1,4 @@
1
- camomilla/__init__.py,sha256=xH1i4zwYfJScQn-ySoem00poi295diBqrVChxqcmkW8,251
1
+ camomilla/__init__.py,sha256=rVNwuy2J-skWorDHQHY5RbR836EfOwG8yJmEDdWPZEE,251
2
2
  camomilla/apps.py,sha256=eUwb9ynyiRAc5OXgt7ZsAdhsCOnPCpNdIFYMheNeN-o,532
3
3
  camomilla/authentication.py,sha256=jz6tQT4PPEu-_JLox1LZrOy7EiWBb9MWaObK63MJGus,855
4
4
  camomilla/context_processors.py,sha256=cGowjDZ-oDGYn1j2Pj5QDGCqnzXAOdOwp5dmzin_FTc,165
@@ -9,7 +9,7 @@ camomilla/model_api.py,sha256=-7l3fc2eN1itCMzkWA8nFaQXMmz0vs7IlGlShF-gSuo,2487
9
9
  camomilla/parsers.py,sha256=fL8XGCGPxJIZNZkPdGtnPSbDP-6-yzGOCVMuLPjkx9Y,1975
10
10
  camomilla/permissions.py,sha256=9NlBO4JMmg36vXCUjPNyq6uZxhkdrnXyIbJVLtWhGWE,1813
11
11
  camomilla/redirects.py,sha256=ilcyHidb5Iw3jTrXMnPntr50kkl_WB3QOB0VNkIxP7A,263
12
- camomilla/settings.py,sha256=nY-a1PRhbQ_edvNG5WyndPLWxwsRb_h4eFAjmOHvKYM,3599
12
+ camomilla/settings.py,sha256=V9rf42MSAwJoGlJS-yAW5pzgGcu5J1g7rrVCAmRtMJU,3763
13
13
  camomilla/sitemap.py,sha256=U2t5TwhB_-sEscmQZ69PZ5st3bIap8NRxzWEvCgB130,786
14
14
  camomilla/translation.py,sha256=_QyfTlKG6hQ_ClRfxzeJ-3oI3Nu5peJN9xFkO9Ib3As,1316
15
15
  camomilla/urls.py,sha256=XgaeFoG2eXlJQve3KmFKlD-74CMLW1ziaY1mq-lrAiA,2095
@@ -60,7 +60,7 @@ camomilla/storages/default.py,sha256=GNzvV_JZpXMcfTkyXjw5CfK8EIBi3o-NXYBO0KAxD5M
60
60
  camomilla/storages/optimize.py,sha256=VGSXZigzZC8LnPTqyTOpPA2Ba9EJB_KC5bcACoRs4GA,2762
61
61
  camomilla/storages/overwrite.py,sha256=jvW3zHvXNzH9dIjeZmmfXo_O3K1ZQmLQzmlSKAOE8ZA,360
62
62
  camomilla/templates/admin/camomilla/page/change_form.html,sha256=ig7rRUtylDZMINBQuVPpZLmeB4sOTV_VtqnTgzAyxEo,251
63
- camomilla/templates/defaults/base.html,sha256=pklt7Pif3g9d7gwgRxCQj7gniJaHD14ZqZID_xIlC0A,6638
63
+ camomilla/templates/defaults/base.html,sha256=C2gCnQP1AkERPv5w00nVhZdfg2h_8DMoIkmdf2I-VVY,8166
64
64
  camomilla/templates/defaults/articles/default.html,sha256=1f89jBvNtTa1mPAbC91yy8CzeAjTWO3hhQsTuQW5OKg,239
65
65
  camomilla/templates/defaults/pages/default.html,sha256=bP81Qb6M56I-fBJMywWwEu_cnERtWIX28UkGrUSRU6M,144
66
66
  camomilla/templates/defaults/parts/langswitch.html,sha256=AkaQzb2KNjRYCMLUn_jE31V36rwBIwp4MneirWPiBcI,3424
@@ -72,8 +72,9 @@ camomilla/templates_context/rendering.py,sha256=GfTR45_gC7WT7zTKPVXkBDwe22uF63A-
72
72
  camomilla/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
73
73
  camomilla/templatetags/camomilla_filters.py,sha256=35x0-cWrRHeLhqypSLlJzEFY_fQDcRHiwZQpFgIsspE,692
74
74
  camomilla/templatetags/menus.py,sha256=7fc4f9DDqtqG6wNb5_Q0km-fq0mqvGnbpR21qO1TJUw,960
75
- camomilla/theme/__init__.py,sha256=3ahSYJ-HBLHrW2VZ45mAM6mlZvpqDKPffxhE0EkD9as,30
76
- camomilla/theme/apps.py,sha256=3nCSZ6d4tkx2aMNNnQQoqX8PGrrMNf4VxCxBV_JXNrA,1040
75
+ camomilla/templatetags/model_extras.py,sha256=mfPab5wQwAiBud4PWyn4lRumdqdniz3OhZy433zm4KQ,2180
76
+ camomilla/theme/__init__.py,sha256=-G_a31a_XYZrR-sYH8Ect9skMQcf0vEfDEKXexJXOIw,30
77
+ camomilla/theme/apps.py,sha256=Ue2H80fbFgxkQyHeU2H0fWs9Y6d-EnHYv4zz824FSRk,1066
77
78
  camomilla/theme/admin/__init__.py,sha256=TALAZaE-gWshSeGc6yy7VahdX5UfeCeoOE9Q5kJCEpM,2270
78
79
  camomilla/theme/admin/pages.py,sha256=y3rL1nwZlytyD-YR_qqLiBAmjCAjkBY3v56V6JdhBvY,1908
79
80
  camomilla/theme/admin/translations.py,sha256=iAjGM1A1aYrsz1FpeybROk6rn3Ddl_oUCwgU5oD8nSw,308
@@ -88,7 +89,7 @@ camomilla/utils/normalization.py,sha256=RDCZtjwpEEwjvfUjQl2bEWFKw7NxTzkXco72VeO2
88
89
  camomilla/utils/query_parser.py,sha256=TUScPzPVVJzaKdqy5NqtMOft3H5Bx6liXTVPM1yjH24,6303
89
90
  camomilla/utils/seo.py,sha256=8p_a_TGgohenpJb094tT4mMxbn2xzW0qDILuTnjNocM,3324
90
91
  camomilla/utils/setters.py,sha256=LV57SM65rL1_ZQkVzk9al_Q13lndVywXLkqgfIvgS0Y,915
91
- camomilla/utils/templates.py,sha256=Lv4-5019cnM30HmdZnYWiU5gxry-eFZVAhwOofGQRDs,598
92
+ camomilla/utils/templates.py,sha256=pgj9vrMypdJEYfvabbWRTu3r498pbjvcLCOSrrsm-sw,924
92
93
  camomilla/utils/translation.py,sha256=w5tvTInDLegWBb1TnDWo09ckKY3K6hajuNNsngZIxPQ,4205
93
94
  camomilla/views/__init__.py,sha256=94QuOnnbfMMb17mruO2ydUt286-8zBmDxEPWrJv5Wog,178
94
95
  camomilla/views/articles.py,sha256=qGxebOA5iTbGGe9PfbH40YBoDPKktH8FJongg6rh2R8,571
@@ -108,7 +109,7 @@ camomilla/views/mixins/optimize.py,sha256=iRPNkoeIIlJugk7DjJhDPaqeX7Opi7TxnUoMDn
108
109
  camomilla/views/mixins/ordering.py,sha256=mh7fqPyVCVJh84Nl2pYFQouzGxa-ANF3Wqv0pCb7OVU,4779
109
110
  camomilla/views/mixins/pagination.py,sha256=NWerBdMyBt4Kswig4fbANqGTzsll8SJdE6a8_UIoueU,5772
110
111
  camomilla/views/mixins/permissions.py,sha256=TPmR3Hoa3BjeJu9rCE_7lpLOAupue4WI42C21HTo6X4,200
111
- django_camomilla_cms-6.0.0b17.dist-info/licenses/LICENSE,sha256=kVS7zDrNkav2hLLXbOJwVdonY2ToApTK3khyJagGQoQ,1063
112
+ django_camomilla_cms-6.0.0b18.dist-info/licenses/LICENSE,sha256=kVS7zDrNkav2hLLXbOJwVdonY2ToApTK3khyJagGQoQ,1063
112
113
  tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
113
114
  tests/test_api.py,sha256=t03EFDezGgm4UJl8RIVvnTUkAGTB6ptm0G2lHBQ7ljc,1833
114
115
  tests/test_camomilla_filters.py,sha256=5LlR3tctGu6qxVmOrY52AGh_ACvEzdAvkwH2v7medpo,1536
@@ -125,8 +126,8 @@ tests/test_utils.py,sha256=o_FG7XOxLePOBfwBr4sk09gej0onWNw9t2-gSjGmgNg,3741
125
126
  tests/fixtures/__init__.py,sha256=NGj22kLV65v56IpOrOVqSkPhJePTXD4QjuuZhZSMwfQ,460
126
127
  tests/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
127
128
  tests/utils/api.py,sha256=TYcDXeILHtBwzwG0acwPFmiqMZnlF9VnLB0Ydhg55vA,865
128
- tests/utils/media.py,sha256=ChsoHqwWmVYHE7teFsV9swTqBEFD0zcvQSBuKsK9G_s,298
129
- django_camomilla_cms-6.0.0b17.dist-info/METADATA,sha256=oiU3jK3ZCjQurWBdq2vaUZgBr2OMim4RLEjSPtIVm28,2565
130
- django_camomilla_cms-6.0.0b17.dist-info/WHEEL,sha256=oSJJyWjO7Z2XSScFQUpXG1HL-N0sFMqqeKVVbZTPkWc,109
131
- django_camomilla_cms-6.0.0b17.dist-info/top_level.txt,sha256=G9VIGBmMMqC7JEckoTgXKmC6T2BR75QRkqRnngw1_lo,16
132
- django_camomilla_cms-6.0.0b17.dist-info/RECORD,,
129
+ tests/utils/media.py,sha256=-cnrQzzVuhNSb5rT5xMUs5f3yYpBnS0fVGDcjgsb8lw,291
130
+ django_camomilla_cms-6.0.0b18.dist-info/METADATA,sha256=BwxjdmoaRTe-4KYk6odNDH53VHDiyBtS5vRA2dHWjIY,2565
131
+ django_camomilla_cms-6.0.0b18.dist-info/WHEEL,sha256=_z0Kb-VmhLeNt2nZ-PsoQBjD25rP0tBwgAyRYD7oTKI,109
132
+ django_camomilla_cms-6.0.0b18.dist-info/top_level.txt,sha256=G9VIGBmMMqC7JEckoTgXKmC6T2BR75QRkqRnngw1_lo,16
133
+ django_camomilla_cms-6.0.0b18.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.3.1)
2
+ Generator: setuptools (80.7.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py2-none-any
5
5
  Tag: py3-none-any
tests/utils/media.py CHANGED
@@ -2,8 +2,9 @@ import os
2
2
  from tests.fixtures import load_asset
3
3
  from django.conf import settings
4
4
 
5
+
5
6
  def load_asset_and_remove_media(filename):
6
- asset = load_asset(filename)
7
- if os.path.exists(f"{settings.MEDIA_ROOT}/{filename}"):
8
- os.remove(f"{settings.MEDIA_ROOT}/{filename}")
9
- return asset
7
+ asset = load_asset(filename)
8
+ if os.path.exists(f"{settings.MEDIA_ROOT}/{filename}"):
9
+ os.remove(f"{settings.MEDIA_ROOT}/{filename}")
10
+ return asset