django-camomilla-cms 5.8.6__py2.py3-none-any.whl → 6.0.0__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.
Files changed (143) hide show
  1. camomilla/__init__.py +8 -2
  2. camomilla/apps.py +9 -1
  3. camomilla/context_processors.py +6 -0
  4. camomilla/contrib/modeltranslation/__init__.py +0 -0
  5. camomilla/contrib/modeltranslation/hvad_migration.py +126 -0
  6. camomilla/dynamic_pages_urls.py +33 -0
  7. camomilla/fields/__init__.py +13 -0
  8. camomilla/{fields.py → fields/json.py} +15 -18
  9. camomilla/management/commands/regenerate_thumbnails.py +0 -1
  10. camomilla/managers/__init__.py +3 -0
  11. camomilla/managers/pages.py +116 -0
  12. camomilla/model_api.py +86 -0
  13. camomilla/models/__init__.py +5 -6
  14. camomilla/models/article.py +26 -44
  15. camomilla/models/content.py +8 -15
  16. camomilla/models/media.py +70 -97
  17. camomilla/models/menu.py +106 -0
  18. camomilla/models/mixins/__init__.py +10 -48
  19. camomilla/models/page.py +521 -20
  20. camomilla/openapi/__init__.py +0 -0
  21. camomilla/openapi/schema.py +67 -0
  22. camomilla/parsers.py +0 -1
  23. camomilla/redirects.py +10 -0
  24. camomilla/serializers/__init__.py +2 -0
  25. camomilla/serializers/article.py +5 -10
  26. camomilla/serializers/base/__init__.py +21 -17
  27. camomilla/serializers/content_type.py +17 -0
  28. camomilla/serializers/fields/__init__.py +6 -20
  29. camomilla/serializers/fields/file.py +5 -0
  30. camomilla/serializers/fields/related.py +24 -4
  31. camomilla/serializers/media.py +6 -8
  32. camomilla/serializers/menu.py +17 -0
  33. camomilla/serializers/mixins/__init__.py +23 -187
  34. camomilla/serializers/mixins/fields.py +20 -0
  35. camomilla/serializers/mixins/filter_fields.py +57 -0
  36. camomilla/serializers/mixins/json.py +34 -0
  37. camomilla/serializers/mixins/language.py +32 -0
  38. camomilla/serializers/mixins/nesting.py +35 -0
  39. camomilla/serializers/mixins/optimize.py +91 -0
  40. camomilla/serializers/mixins/ordering.py +34 -0
  41. camomilla/serializers/mixins/page.py +58 -0
  42. camomilla/serializers/mixins/translation.py +103 -0
  43. camomilla/serializers/page.py +53 -4
  44. camomilla/serializers/user.py +5 -4
  45. camomilla/serializers/utils.py +38 -0
  46. camomilla/serializers/validators.py +51 -0
  47. camomilla/settings.py +118 -0
  48. camomilla/sitemap.py +30 -0
  49. camomilla/storages/__init__.py +4 -0
  50. camomilla/storages/default.py +12 -0
  51. camomilla/storages/optimize.py +71 -0
  52. camomilla/{storages.py → storages/overwrite.py} +2 -2
  53. camomilla/templates/admin/camomilla/page/change_form.html +10 -0
  54. camomilla/templates/defaults/articles/default.html +7 -0
  55. camomilla/templates/defaults/base.html +170 -0
  56. camomilla/templates/defaults/pages/default.html +3 -0
  57. camomilla/templates/defaults/parts/langswitch.html +83 -0
  58. camomilla/templates/defaults/parts/menu.html +15 -0
  59. camomilla/templates_context/__init__.py +0 -0
  60. camomilla/templates_context/autodiscover.py +51 -0
  61. camomilla/templates_context/rendering.py +89 -0
  62. camomilla/templatetags/camomilla_filters.py +6 -5
  63. camomilla/templatetags/menus.py +37 -0
  64. camomilla/templatetags/model_extras.py +77 -0
  65. camomilla/theme/__init__.py +1 -1
  66. camomilla/theme/admin/__init__.py +99 -0
  67. camomilla/theme/admin/pages.py +46 -0
  68. camomilla/theme/admin/translations.py +13 -0
  69. camomilla/theme/apps.py +38 -0
  70. camomilla/theme/static/admin/css/responsive.css +5 -1021
  71. camomilla/theme/static/admin/img/favicon.ico +0 -0
  72. camomilla/theme/static/admin/img/logo.svg +31 -0
  73. camomilla/theme/templates/admin/base.html +7 -0
  74. camomilla/theme/templates/rosetta/base.html +196 -0
  75. camomilla/translation.py +61 -0
  76. camomilla/urls.py +38 -17
  77. camomilla/utils/__init__.py +4 -0
  78. camomilla/utils/getters.py +27 -0
  79. camomilla/utils/normalization.py +7 -0
  80. camomilla/utils/query_parser.py +167 -0
  81. camomilla/{utils.py → utils/seo.py} +13 -15
  82. camomilla/utils/setters.py +37 -0
  83. camomilla/utils/templates.py +32 -0
  84. camomilla/utils/translation.py +114 -0
  85. camomilla/views/__init__.py +1 -1
  86. camomilla/views/articles.py +5 -7
  87. camomilla/views/base/__init__.py +35 -5
  88. camomilla/views/contents.py +6 -11
  89. camomilla/views/decorators.py +26 -0
  90. camomilla/views/medias.py +24 -19
  91. camomilla/views/menus.py +81 -0
  92. camomilla/views/mixins/__init__.py +17 -73
  93. camomilla/views/mixins/bulk_actions.py +22 -0
  94. camomilla/views/mixins/language.py +33 -0
  95. camomilla/views/mixins/optimize.py +18 -0
  96. camomilla/views/mixins/ordering.py +2 -2
  97. camomilla/views/mixins/pagination.py +12 -18
  98. camomilla/views/mixins/permissions.py +6 -0
  99. camomilla/views/pages.py +28 -6
  100. camomilla/views/tags.py +5 -6
  101. camomilla/views/users.py +7 -12
  102. django_camomilla_cms-6.0.0.dist-info/METADATA +123 -0
  103. django_camomilla_cms-6.0.0.dist-info/RECORD +133 -0
  104. {django_camomilla_cms-5.8.6.dist-info → django_camomilla_cms-6.0.0.dist-info}/WHEEL +1 -1
  105. tests/fixtures/__init__.py +14 -0
  106. tests/test_api.py +22 -39
  107. tests/test_camomilla_filters.py +11 -13
  108. tests/test_media.py +152 -0
  109. tests/test_menu.py +112 -0
  110. tests/test_model_api.py +113 -0
  111. tests/test_model_api_permissions.py +44 -0
  112. tests/test_model_api_register.py +355 -0
  113. tests/test_pages.py +351 -0
  114. tests/test_query_parser.py +58 -0
  115. tests/test_templates_context.py +149 -0
  116. tests/test_utils.py +64 -64
  117. tests/utils/__init__.py +0 -0
  118. tests/utils/api.py +28 -0
  119. tests/utils/media.py +10 -0
  120. camomilla/admin.py +0 -98
  121. camomilla/migrations/0001_initial.py +0 -577
  122. camomilla/migrations/0002_auto_20200214_1127.py +0 -33
  123. camomilla/migrations/0003_auto_20210130_1610.py +0 -30
  124. camomilla/migrations/0004_auto_20210511_0937.py +0 -25
  125. camomilla/migrations/0005_media_image_props.py +0 -19
  126. camomilla/migrations/0006_auto_20220103_1845.py +0 -35
  127. camomilla/migrations/0007_auto_20220211_1622.py +0 -18
  128. camomilla/migrations/0008_auto_20220309_1616.py +0 -60
  129. camomilla/migrations/0009_article__hvad_query_category__hvad_query_and_more.py +0 -165
  130. camomilla/migrations/0010_auto_20220802_1406.py +0 -83
  131. camomilla/migrations/0011_auto_20220902_1000.py +0 -15
  132. camomilla/models/category.py +0 -25
  133. camomilla/models/tag.py +0 -19
  134. camomilla/theme/static/admin/img/logo.png +0 -0
  135. camomilla/theme/templates/admin/base_site.html +0 -18
  136. camomilla/views/categories.py +0 -13
  137. django_camomilla_cms-5.8.6.dist-info/METADATA +0 -63
  138. django_camomilla_cms-5.8.6.dist-info/RECORD +0 -76
  139. tests/urls.py +0 -21
  140. /camomilla/{migrations → contrib}/__init__.py +0 -0
  141. /camomilla/templates/{camomilla → defaults}/widgets/media_select_multiple.html +0 -0
  142. {django_camomilla_cms-5.8.6.dist-info → django_camomilla_cms-6.0.0.dist-info/licenses}/LICENSE +0 -0
  143. {django_camomilla_cms-5.8.6.dist-info → django_camomilla_cms-6.0.0.dist-info}/top_level.txt +0 -0
tests/test_pages.py ADDED
@@ -0,0 +1,351 @@
1
+ import pytest
2
+ from django.test import TestCase
3
+ from rest_framework.test import APIClient
4
+ from .utils.api import login_superuser
5
+ from camomilla.models import Page
6
+ from camomilla.models.page import UrlRedirect
7
+ from datetime import datetime, timedelta
8
+
9
+
10
+ class PagesTestCase(TestCase):
11
+ def setUp(self):
12
+ self.client = APIClient()
13
+ token = login_superuser()
14
+ self.client.credentials(HTTP_AUTHORIZATION="Token " + token)
15
+
16
+ @pytest.mark.django_db
17
+ def test_pages_api_no_access(self):
18
+ client = APIClient()
19
+ response = client.post("/api/camomilla/pages/")
20
+ assert response.status_code == 401
21
+
22
+ @pytest.mark.django_db
23
+ def test_pages_api_crud(self):
24
+ # Create page
25
+ response = self.client.post(
26
+ "/api/camomilla/pages/",
27
+ {
28
+ "translations": {
29
+ "it": {
30
+ "title": "title_page_1",
31
+ }
32
+ }
33
+ },
34
+ format="json",
35
+ )
36
+
37
+ assert response.status_code == 201
38
+ assert len(Page.objects.all()) == 1
39
+ page = Page.objects.first()
40
+ assert page.id == 1
41
+ assert page.title_it == "title_page_1"
42
+ assert page.url_node.id == 1
43
+
44
+ response = self.client.post(
45
+ "/api/camomilla/pages/",
46
+ {
47
+ "title_it": "title_page_2",
48
+ },
49
+ )
50
+
51
+ assert response.status_code == 201
52
+ assert len(Page.objects.all()) == 2
53
+ page = Page.objects.last()
54
+ assert page.id == 2
55
+ assert page.title_it == "title_page_2"
56
+ assert page.url_node.id == 2
57
+
58
+ # Update page
59
+ response = self.client.patch(
60
+ "/api/camomilla/pages/2/",
61
+ {
62
+ "translations": {
63
+ "it": {
64
+ "title": "title_page_2_updated",
65
+ }
66
+ }
67
+ },
68
+ format="json",
69
+ )
70
+
71
+ assert response.status_code == 200
72
+ assert len(Page.objects.all()) == 2
73
+ page = Page.objects.last()
74
+ assert page.id == 2
75
+ assert page.title_it == "title_page_2_updated"
76
+
77
+ # Read page
78
+ response = self.client.get("/api/camomilla/pages/2/")
79
+
80
+ assert response.status_code == 200
81
+ assert response.json()["id"] == 2
82
+ assert response.json()["title"] == "title_page_2_updated"
83
+
84
+ # Read pages
85
+ response = self.client.get("/api/camomilla/pages/")
86
+
87
+ assert response.status_code == 200
88
+ assert response.json()[0]["id"] == 1
89
+ assert response.json()[0]["title"] == "title_page_1"
90
+ assert response.json()[1]["id"] == 2
91
+ assert response.json()[1]["title"] == "title_page_2_updated"
92
+
93
+ # Delete page
94
+ response = self.client.delete("/api/camomilla/pages/2/")
95
+
96
+ assert response.status_code == 204
97
+ assert len(Page.objects.all()) == 1
98
+ page = Page.objects.last()
99
+ assert page.id == 1
100
+ assert page.title_it == "title_page_1"
101
+
102
+ @pytest.mark.django_db
103
+ def test_pages_url_nodes(self):
104
+ # Create page with automatic url creation
105
+ response = self.client.post(
106
+ "/api/camomilla/pages/",
107
+ {
108
+ "title_en": "title_page_1",
109
+ "title_it": "titolo_pagina_1",
110
+ },
111
+ )
112
+
113
+ assert response.status_code == 201
114
+
115
+ # EN automatic url creation
116
+ response = self.client.get("/api/camomilla/pages/1/?language=en")
117
+ assert response.json()["autopermalink"] == True
118
+ assert response.json()["permalink"] == "/title_page_1"
119
+ # IT automatic url creation
120
+ response = self.client.get("/api/camomilla/pages/1/?language=it")
121
+ assert response.json()["autopermalink"] == True
122
+ assert response.json()["permalink"] == "/titolo_pagina_1"
123
+
124
+ # Create page with manual url creation
125
+ response = self.client.post(
126
+ "/api/camomilla/pages/",
127
+ {
128
+ "translations": {
129
+ "it": {
130
+ "title": "titolo_pagina_2",
131
+ "permalink": "permalink_manuale_it_2",
132
+ "autopermalink": False,
133
+ },
134
+ "en": {
135
+ "title": "title_page_2",
136
+ "permalink": "permalink_manual_en_2",
137
+ "autopermalink": False,
138
+ },
139
+ }
140
+ },
141
+ format="json",
142
+ )
143
+ assert response.status_code == 201
144
+
145
+ # EN manual url creation
146
+ response = self.client.get("/api/camomilla/pages/2/?language=en")
147
+ assert response.json()["autopermalink"] == False
148
+ assert response.json()["permalink"] == "/permalink_manual_en_2"
149
+ # IT manual url creation
150
+ response = self.client.get("/api/camomilla/pages/2/?language=it")
151
+ assert response.json()["autopermalink"] == False
152
+ assert response.json()["permalink"] == "/permalink_manuale_it_2"
153
+
154
+ # Create page with a parent page with automatic url creation
155
+ response = self.client.post(
156
+ "/api/camomilla/pages/",
157
+ {
158
+ "title_en": "title_page_3",
159
+ "title_it": "titolo_pagina_3",
160
+ "parent_page": 2,
161
+ },
162
+ )
163
+ assert response.status_code == 201
164
+
165
+ # EN parent page with automatic url creation
166
+ response = self.client.get("/api/camomilla/pages/3/?language=en")
167
+ assert response.json()["autopermalink"] == True
168
+ assert response.json()["permalink"] == "//permalink_manual_en_2/title_page_3"
169
+ # IT parent page with automatic url creation
170
+ response = self.client.get("/api/camomilla/pages/3/?language=it")
171
+ assert response.json()["autopermalink"] == True
172
+ assert (
173
+ response.json()["permalink"] == "//permalink_manuale_it_2/titolo_pagina_3"
174
+ )
175
+
176
+ # Check url uniqueness and consistency EN
177
+ response = self.client.post(
178
+ "/api/camomilla/pages/",
179
+ {
180
+ "autopermalink_en": False,
181
+ "permalink_en": "permalink_manual_en_2",
182
+ },
183
+ )
184
+
185
+ # Client error when url check uniqueness and consistency fail
186
+ assert response.status_code == 400
187
+ assert (
188
+ response.data["permalink_en"][0]
189
+ == "There is an other page with same permalink."
190
+ )
191
+
192
+ # Check url uniqueness and consistency IT
193
+ response = self.client.post(
194
+ "/api/camomilla/pages/",
195
+ {
196
+ "translations": {
197
+ "it": {
198
+ "autopermalink": False,
199
+ "permalink": "permalink_manuale_it_2",
200
+ }
201
+ }
202
+ },
203
+ format="json",
204
+ )
205
+
206
+ # Client error when url check uniqueness and consistency fail
207
+ assert response.status_code == 400
208
+ assert (
209
+ response.data["permalink_it"][0]
210
+ == "There is an other page with same permalink."
211
+ )
212
+
213
+ @pytest.mark.django_db
214
+ def test_pages_url_nodes_navigation(self):
215
+ # Test the camomilla.dynamic_pages_url handler for navigating and rendering UrlNodes
216
+ self.client.post(
217
+ "/api/camomilla/pages/",
218
+ {
219
+ "autopermalink_en": False,
220
+ "permalink_en": "permalink_4_en",
221
+ "status_en": "PUB",
222
+ "autopermalink_it": False,
223
+ "permalink_it": "permalink_4_it",
224
+ "status_it": "PUB",
225
+ },
226
+ )
227
+
228
+ response = self.client.get("/permalink_4_en/")
229
+ assert response.status_code == 200
230
+ response = self.client.get("/it/permalink_4_it/")
231
+ assert response.status_code == 200
232
+
233
+ # Test draft - published - planned and ?preview=true
234
+ self.client.post(
235
+ "/api/camomilla/pages/",
236
+ {
237
+ "translations": {
238
+ "it": {
239
+ "autopermalink": False,
240
+ "permalink": "permalink_5_it",
241
+ "status": "PLA",
242
+ },
243
+ "en": {
244
+ "autopermalink": False,
245
+ "permalink": "permalink_5_en",
246
+ "status": "DRF",
247
+ },
248
+ }
249
+ },
250
+ format="json",
251
+ )
252
+
253
+ response = self.client.get("/permalink_5_en/")
254
+ assert response.status_code == 404
255
+ response = self.client.get("/permalink_5_en/?preview=true")
256
+ assert response.status_code == 200
257
+ response = self.client.get("/it/permalink_5_it/")
258
+ assert response.status_code == 404
259
+
260
+ self.client.patch(
261
+ "/api/camomilla/pages/2/",
262
+ {
263
+ "publication_date": (datetime.now() - timedelta(1)).strftime("%Y-%m-%d")
264
+ + " 00:00:00",
265
+ },
266
+ )
267
+
268
+ response = self.client.get("/it/permalink_5_it/")
269
+ assert response.status_code == 200
270
+
271
+ @pytest.mark.django_db
272
+ def test_pages_url_nodes_navigation_redirects(self):
273
+ # Test the camomilla.dynamic_pages_url handler for navigating and rendering UrlNodes
274
+ self.client.post(
275
+ "/api/camomilla/pages/",
276
+ {
277
+ "translations": {
278
+ "it": {
279
+ "autopermalink": False,
280
+ "permalink": "permalink_6_it",
281
+ "status": "PUB",
282
+ },
283
+ "en": {
284
+ "autopermalink": False,
285
+ "permalink": "permalink_6_en",
286
+ "status": "PUB",
287
+ },
288
+ }
289
+ },
290
+ format="json",
291
+ )
292
+
293
+ # EN Insert Moved Permanently Redirect
294
+ url_redirect = UrlRedirect.objects.create(
295
+ language_code="en",
296
+ from_url="/redirecting_1",
297
+ to_url="/redirected_1",
298
+ url_node_id=1,
299
+ )
300
+
301
+ response = self.client.get("/redirecting_1/")
302
+ assert response.status_code == 301
303
+ assert response.url == "/redirected_1/"
304
+
305
+ # EN Change to Moved Temporarily Redirect
306
+ url_redirect.permanent = False
307
+ url_redirect.save()
308
+ response = self.client.get("/redirecting_1/")
309
+ assert response.status_code == 302
310
+
311
+ # IT Insert Moved Permanently Redirect
312
+ url_redirect = UrlRedirect.objects.create(
313
+ language_code="it",
314
+ from_url="/urlreindirizzamento_1",
315
+ to_url="/urlreindirizzato_1",
316
+ url_node_id=1,
317
+ )
318
+
319
+ response = self.client.get("/it/urlreindirizzamento_1/")
320
+ assert response.status_code == 301
321
+ assert response.url == "/it/urlreindirizzato_1/"
322
+
323
+ # IT Change to Moved Temporarily Redirect
324
+ url_redirect.permanent = False
325
+ url_redirect.save()
326
+ response = self.client.get("/it/urlreindirizzamento_1/")
327
+ assert response.status_code == 302
328
+
329
+ # Test auto redirect after permalink change
330
+ self.client.patch(
331
+ "/api/camomilla/pages/1/",
332
+ {
333
+ "translations": {
334
+ "it": {
335
+ "permalink": "permalink_6_it_changed",
336
+ },
337
+ "en": {
338
+ "permalink": "permalink_6_en_changed",
339
+ },
340
+ }
341
+ },
342
+ format="json",
343
+ )
344
+
345
+ response = self.client.get("/permalink_6_en/")
346
+ assert response.status_code == 301
347
+ assert response.url == "/permalink_6_en_changed/"
348
+
349
+ response = self.client.get("/it/permalink_6_it/")
350
+ assert response.status_code == 301
351
+ assert response.url == "/it/permalink_6_it_changed/"
@@ -0,0 +1,58 @@
1
+ import pytest
2
+ from django.db.models import Q
3
+ from camomilla.utils.query_parser import (
4
+ ConditionParser,
5
+ )
6
+
7
+
8
+ @pytest.mark.parametrize(
9
+ "query, expected_q",
10
+ [
11
+ # Single condition
12
+ ("name__icontains=foo", Q(name__icontains="foo")),
13
+ # Multiple conditions with AND
14
+ (
15
+ "name__icontains=foo AND age__gt=21",
16
+ Q(name__icontains="foo") & Q(age__gt=21),
17
+ ),
18
+ # Multiple conditions with OR
19
+ (
20
+ "name__icontains=foo OR name__icontains=bar",
21
+ Q(name__icontains="foo") | Q(name__icontains="bar"),
22
+ ),
23
+ # Mixed AND and OR conditions
24
+ (
25
+ "name__icontains=foo OR (age__gt=21 AND city__iexact='New York')",
26
+ Q(name__icontains="foo") | (Q(age__gt=21) & Q(city__iexact="New York")),
27
+ ),
28
+ # Nested parentheses with OR inside AND
29
+ (
30
+ "((name__icontains=foo) OR (name__icontains=bar)) AND name__icontains=baz",
31
+ (Q(name__icontains="foo") | Q(name__icontains="bar"))
32
+ & Q(name__icontains="baz"),
33
+ ),
34
+ # Complex nested conditions with multiple OR and AND
35
+ (
36
+ "((name__icontains=foo) OR (name__icontains=bar) OR (name__icontains=buz)) AND age__gt=25 AND city__iexact='LA'",
37
+ (
38
+ Q(name__icontains="foo")
39
+ | Q(name__icontains="bar")
40
+ | Q(name__icontains="buz")
41
+ )
42
+ & Q(age__gt=25)
43
+ & Q(city__iexact="LA"),
44
+ ),
45
+ # Single condition with no parentheses
46
+ ("age__lte=30", Q(age__lte=30)),
47
+ # Simple nested OR and AND with more levels of nesting
48
+ (
49
+ "(name__icontains=foo AND (age__gt=21 OR city__iexact='New York')) OR country__iexact='US'",
50
+ (Q(name__icontains="foo") & (Q(age__gt=21) | Q(city__iexact="New York")))
51
+ | Q(country__iexact="US"),
52
+ ),
53
+ ],
54
+ )
55
+ def test_condition_parser(query, expected_q):
56
+ parser = ConditionParser(query)
57
+ q_object = parser.parse_to_q()
58
+ assert q_object.__str__() == expected_q.__str__()
@@ -0,0 +1,149 @@
1
+ import pytest
2
+ import json
3
+ import re
4
+ from django.test import TestCase
5
+ from rest_framework.test import APIClient
6
+ from .utils.api import login_superuser
7
+ from .utils.media import load_asset_and_remove_media
8
+
9
+
10
+ class TemoplateContextTestCase(TestCase):
11
+ def setUp(self):
12
+ self.client = APIClient()
13
+ token = login_superuser()
14
+ self.client.credentials(HTTP_AUTHORIZATION="Token " + token)
15
+
16
+ @pytest.mark.django_db
17
+ def test_page_context_template_based(self):
18
+ # Create page with custom context template
19
+ response = self.client.post(
20
+ "/api/camomilla/pages/",
21
+ {
22
+ "title_en": "Page custom context template",
23
+ "autopermalink_en": False,
24
+ "permalink_en": "permalink_context_template",
25
+ "template": "website/page_context_template_based.html",
26
+ "status_en": "PUB",
27
+ },
28
+ format="multipart",
29
+ )
30
+ assert response.status_code == 201
31
+
32
+ # Create media for custom context
33
+ asset = load_asset_and_remove_media("10595073.png")
34
+ response = self.client.post(
35
+ "/api/camomilla/media/",
36
+ {
37
+ "file": asset,
38
+ "data": json.dumps(
39
+ {
40
+ "translations": {
41
+ "en": {
42
+ "alt_text": "Test media",
43
+ "title": "Test media",
44
+ "description": "Description media",
45
+ }
46
+ }
47
+ }
48
+ ),
49
+ },
50
+ format="multipart",
51
+ )
52
+ assert response.status_code == 201
53
+
54
+ response = self.client.get("/permalink_context_template/")
55
+ assert response.status_code == 200
56
+ assert (
57
+ re.sub(r"[\s+]", "", response.content.decode())
58
+ == "<!DOCTYPEhtml><html><body><h1>Titlepageforpagecontexttemplatebased</h1><p>Contentpageforpagecontexttemplatebased</p><ul><li>Testmedia</li></ul></body></html>"
59
+ )
60
+
61
+ @pytest.mark.django_db
62
+ def test_model_context_template_based(self):
63
+ # Create page with custom context template
64
+ response = self.client.post(
65
+ "/api/camomilla/pages/",
66
+ {
67
+ "title_en": "Page custom context template",
68
+ "autopermalink_en": False,
69
+ "permalink_en": "permalink_context_template",
70
+ "template": "website/page_context_model_based.html",
71
+ "status_en": "PUB",
72
+ },
73
+ format="multipart",
74
+ )
75
+ assert response.status_code == 201
76
+
77
+ # Create media for custom context
78
+ asset = load_asset_and_remove_media("10595073.png")
79
+ response = self.client.post(
80
+ "/api/camomilla/media/",
81
+ {
82
+ "file": asset,
83
+ "data": json.dumps(
84
+ {
85
+ "translations": {
86
+ "en": {
87
+ "alt_text": "Test media",
88
+ "title": "Test media",
89
+ "description": "Description media",
90
+ }
91
+ }
92
+ }
93
+ ),
94
+ },
95
+ format="multipart",
96
+ )
97
+ assert response.status_code == 201
98
+
99
+ response = self.client.get("/permalink_context_template/")
100
+ assert response.status_code == 200
101
+ assert (
102
+ re.sub(r"[\s+]", "", response.content.decode())
103
+ == "<!DOCTYPEhtml><html><body><h1>Titlepageforpagecontextmodelbased</h1><p>Contentpageforpagecontextmodelbased</p><ul><li>Testmedia</li></ul></body></html>"
104
+ )
105
+
106
+ @pytest.mark.django_db
107
+ def test_mixed_context_template(self):
108
+ # Create page with custom context template
109
+ response = self.client.post(
110
+ "/api/camomilla/pages/",
111
+ {
112
+ "title_en": "Page custom context template",
113
+ "autopermalink_en": False,
114
+ "permalink_en": "permalink_context_template",
115
+ "template": "website/page_context_mixed.html",
116
+ "status_en": "PUB",
117
+ },
118
+ format="multipart",
119
+ )
120
+ assert response.status_code == 201
121
+
122
+ # Create media for custom context
123
+ asset = load_asset_and_remove_media("10595073.png")
124
+ response = self.client.post(
125
+ "/api/camomilla/media/",
126
+ {
127
+ "file": asset,
128
+ "data": json.dumps(
129
+ {
130
+ "translations": {
131
+ "en": {
132
+ "alt_text": "Test media",
133
+ "title": "Test media",
134
+ "description": "Description media",
135
+ }
136
+ }
137
+ }
138
+ ),
139
+ },
140
+ format="multipart",
141
+ )
142
+ assert response.status_code == 201
143
+
144
+ response = self.client.get("/permalink_context_template/")
145
+ assert response.status_code == 200
146
+ assert (
147
+ re.sub(r"[\s+]", "", response.content.decode())
148
+ == "<!DOCTYPEhtml><html><body><!--Templatecontext--><h1>Titlepageforpagecontexttemplatebased</h1><p>Contentpageforpagecontexttemplatebased</p><ul><li>Testmedia</li></ul><!--Modelcontext--><h1>Titlepageforpagecontextmodelbased</h1><p>Contentpageforpagecontextmodelbased</p><ul><li>Testmedia</li></ul></body></html>"
149
+ )