django-camomilla-cms 6.0.1__py2.py3-none-any.whl → 6.1.1__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.1"
1
+ __version__ = "6.1.1"
2
2
 
3
3
 
4
4
  def get_core_apps():
camomilla/models/menu.py CHANGED
@@ -74,7 +74,7 @@ class MenuNode(BaseModel):
74
74
 
75
75
 
76
76
  class Menu(models.Model):
77
- key = models.CharField(max_length=200, unique=True, editable=False)
77
+ key = models.CharField(max_length=200, unique=True, editable=True, default=uuid4)
78
78
  available_classes = models.JSONField(default=dict, editable=False)
79
79
  enabled = models.BooleanField(default=True)
80
80
  nodes = StructuredJSONField(default=list, schema=MenuNode)
camomilla/models/page.py CHANGED
@@ -29,6 +29,7 @@ from camomilla import settings
29
29
  from camomilla.templates_context.rendering import ctx_registry
30
30
  from django.conf import settings as django_settings
31
31
  from modeltranslation.utils import build_localized_fieldname
32
+ from django.utils.module_loading import import_string
32
33
 
33
34
 
34
35
  class UrlRedirect(models.Model):
@@ -287,6 +288,22 @@ class AbstractPage(SeoMixin, MetaMixin, models.Model, metaclass=PageBase):
287
288
  context.update(new_ctx)
288
289
  return ctx_registry.get_context_for_page(self, request, super_ctx=context)
289
290
 
291
+ @classmethod
292
+ def get_serializer(cls):
293
+ from camomilla.serializers.mixins import AbstractPageMixin
294
+
295
+ standard_serializer = (
296
+ pointed_getter(cls, "_page_meta.standard_serializer")
297
+ or settings.PAGES_DEFAULT_SERIALIZER
298
+ )
299
+ if isinstance(standard_serializer, str):
300
+ standard_serializer = import_string(standard_serializer)
301
+ if not issubclass(standard_serializer, AbstractPageMixin):
302
+ raise ValueError(
303
+ f"Standard serializer {standard_serializer} must be a subclass of AbstractPageMixin"
304
+ )
305
+ return standard_serializer
306
+
290
307
  @property
291
308
  def model_name(self) -> str:
292
309
  return self._meta.app_label + "." + self._meta.model_name
@@ -326,7 +343,7 @@ class AbstractPage(SeoMixin, MetaMixin, models.Model, metaclass=PageBase):
326
343
  def childs(self) -> models.Manager:
327
344
  if hasattr(self._page_meta, "child_page_field"):
328
345
  return getattr(self, self._page_meta.child_page_field)
329
- return getattr(self, PAGE_CHILD_RELATED_NAME % self.model_info)
346
+ return getattr(self, PAGE_CHILD_RELATED_NAME % self.model_info, self.__class__.objects.none())
330
347
 
331
348
  @property
332
349
  def parent(self) -> models.Model:
@@ -356,7 +373,8 @@ class AbstractPage(SeoMixin, MetaMixin, models.Model, metaclass=PageBase):
356
373
  def generate_permalink(self, safe: bool = True) -> str:
357
374
  permalink = f"/{slugify(self.title or '', allow_unicode=True)}"
358
375
  if self.parent:
359
- permalink = f"/{self.parent.permalink}{permalink}"
376
+ parent_permalink = (self.parent.permalink or "").lstrip("/")
377
+ permalink = f"/{parent_permalink}{permalink}"
360
378
  set_nofallbacks(self, "permalink", permalink)
361
379
  qs = UrlNode.objects.exclude(pk=getattr(self.url_node or object, "pk", None))
362
380
  if safe and qs.filter(permalink=permalink).exists():
@@ -455,7 +473,7 @@ class AbstractPage(SeoMixin, MetaMixin, models.Model, metaclass=PageBase):
455
473
  preview = request and getattr(request, "GET", {}).get("preview", False)
456
474
  permalinks = get_field_translations(self.url_node or object, "permalink", None)
457
475
  for lang in activate_languages():
458
- if lang in permalinks:
476
+ if lang in permalinks and permalinks[lang]:
459
477
  permalinks[lang] = (
460
478
  UrlNode.reverse_url(permalinks[lang])
461
479
  if preview or self.is_public
@@ -475,6 +493,7 @@ class AbstractPage(SeoMixin, MetaMixin, models.Model, metaclass=PageBase):
475
493
  parent_page_field = "parent_page"
476
494
  default_template = settings.PAGE_DEFAULT_TEMPLATE
477
495
  inject_context_func = settings.PAGE_INJECT_CONTEXT_FUNC
496
+ standard_serializer = settings.PAGES_DEFAULT_SERIALIZER
478
497
 
479
498
 
480
499
  class Page(AbstractPage):
@@ -22,7 +22,7 @@ class PageSerializer(AbstractPageMixin, BaseModelSerializer):
22
22
  fields = "__all__"
23
23
 
24
24
 
25
- class BasicUrlNodeSerializer(BaseModelSerializer):
25
+ class UrlNodeSerializer(BaseModelSerializer):
26
26
  is_public = serializers.SerializerMethodField()
27
27
  status = serializers.SerializerMethodField()
28
28
  indexable = serializers.SerializerMethodField()
@@ -41,17 +41,18 @@ class BasicUrlNodeSerializer(BaseModelSerializer):
41
41
  return instance.page.indexable
42
42
 
43
43
 
44
- class UrlNodeSerializer(BasicUrlNodeSerializer):
44
+ class RouteSerializer(UrlNodeSerializer):
45
45
  alternates = serializers.SerializerMethodField()
46
46
 
47
47
  def get_alternates(self, instance: UrlNode):
48
48
  return instance.page.alternate_urls()
49
49
 
50
50
  def to_representation(self, instance: UrlNode):
51
+ standard_serializer = instance.page.get_serializer()
51
52
  model_serializer = build_standard_model_serializer(
52
53
  instance.page.__class__,
53
54
  depth=10,
54
- bases=(AbstractPageMixin,) + get_standard_bases(),
55
+ bases=(standard_serializer,) + get_standard_bases(),
55
56
  )
56
57
  return {
57
58
  **super().to_representation(instance),
camomilla/settings.py CHANGED
@@ -49,6 +49,12 @@ PAGE_INJECT_CONTEXT_FUNC = pointed_getter(
49
49
  django_settings, "CAMOMILLA.RENDER.PAGE.INJECT_CONTEXT", None
50
50
  )
51
51
 
52
+ PAGES_DEFAULT_SERIALIZER = pointed_getter(
53
+ django_settings,
54
+ "CAMOMILLA.API.PAGES.DEFAULT_SERIALIZER",
55
+ "camomilla.serializers.mixins.AbstractPageMixin",
56
+ )
57
+
52
58
  ENABLE_TRANSLATIONS = (
53
59
  ENABLE_REGISTRATIONS and "modeltranslation" in django_settings.INSTALLED_APPS
54
60
  )
@@ -127,6 +133,6 @@ DEBUG = pointed_getter(django_settings, "CAMOMILLA.DEBUG", django_settings.DEBUG
127
133
  # "URL": "http://localhost:4321"
128
134
  # }
129
135
  # }
130
- # "API": {"NESTING_DEPTH": 10, "TRANSLATION_ACCESSOR": "translations"},
136
+ # "API": {"NESTING_DEPTH": 10, "TRANSLATION_ACCESSOR": "translations", "PAGES": {"DEFAULT_SERIALIZER": "camomilla.serializers.page.RouteSerializer"}},
131
137
  # "DEBUG": False
132
138
  # }
@@ -1 +1 @@
1
- __version__ = "6.0.1"
1
+ __version__ = "6.1.1"
camomilla/urls.py CHANGED
@@ -19,7 +19,7 @@ from camomilla.views import (
19
19
  UserViewSet,
20
20
  MenuViewSet,
21
21
  )
22
- from camomilla.views.pages import fetch_page
22
+ from camomilla.views.pages import pages_router
23
23
  from camomilla.redirects import url_patterns as old_redirects
24
24
 
25
25
  router = routers.DefaultRouter()
@@ -37,8 +37,8 @@ router.register(r"menus", MenuViewSet, "camomilla-menus")
37
37
  urlpatterns = [
38
38
  *old_redirects,
39
39
  path("", include(router.urls)),
40
- path("pages-router/", fetch_page),
41
- path("pages-router/<path:permalink>", fetch_page),
40
+ path("pages-router/", pages_router),
41
+ path("pages-router/<path:permalink>", pages_router),
42
42
  path("token-auth/", CamomillaObtainAuthToken.as_view(), name="api_token"),
43
43
  path("auth/login/", CamomillaAuthLogin.as_view(), name="login"),
44
44
  path("auth/logout/", CamomillaAuthLogout.as_view(), name="logout"),
camomilla/views/menus.py CHANGED
@@ -9,7 +9,7 @@ from camomilla.models import AbstractPage, Menu
9
9
  from camomilla.models.page import UrlNode
10
10
  from camomilla.permissions import CamomillaBasePermissions
11
11
  from camomilla.serializers import ContentTypeSerializer, MenuSerializer
12
- from camomilla.serializers.page import BasicUrlNodeSerializer
12
+ from camomilla.serializers.page import UrlNodeSerializer
13
13
  from camomilla.views.base import BaseModelViewset
14
14
  from camomilla.views.decorators import active_lang
15
15
 
@@ -66,7 +66,12 @@ class MenuViewSet(BaseModelViewset):
66
66
  raise Http404("No object matches the given query.")
67
67
  return Response(
68
68
  [
69
- {"id": obj.pk, "name": str(obj), "url_node_id": obj.url_node.pk}
69
+ {
70
+ "id": obj.pk,
71
+ "name": str(obj),
72
+ "url_node_id": obj.url_node.pk,
73
+ "model": f"{content_type.app_label}.{content_type.model}",
74
+ }
70
75
  for obj in content_type.model_class().objects.exclude(
71
76
  url_node__isnull=True
72
77
  )
@@ -78,4 +83,4 @@ class MenuViewSet(BaseModelViewset):
78
83
  def search_urlnode(self, request, *args, **kwargs):
79
84
  url_node = request.GET.get("q", "")
80
85
  qs = UrlNode.objects.filter(permalink__icontains=url_node).order_by("permalink")
81
- return Response(BasicUrlNodeSerializer(qs, many=True).data)
86
+ return Response(UrlNodeSerializer(qs, many=True).data)
camomilla/views/pages.py CHANGED
@@ -2,7 +2,7 @@ from camomilla.models import Page
2
2
  from camomilla.models.page import UrlNode, UrlRedirect
3
3
  from camomilla.permissions import CamomillaBasePermissions
4
4
  from camomilla.serializers import PageSerializer
5
- from camomilla.serializers.page import UrlNodeSerializer
5
+ from camomilla.serializers.page import RouteSerializer
6
6
  from camomilla.views.base import BaseModelViewset
7
7
  from camomilla.views.decorators import active_lang
8
8
  from camomilla.views.mixins import BulkDeleteMixin, GetUserLanguageMixin
@@ -26,10 +26,10 @@ class PageViewSet(GetUserLanguageMixin, BulkDeleteMixin, BaseModelViewset):
26
26
  permissions.AllowAny,
27
27
  ]
28
28
  )
29
- def fetch_page(request, permalink=""):
29
+ def pages_router(request, permalink=""):
30
30
  redirect = UrlRedirect.find_redirect_from_url(f"/{permalink}")
31
31
  if redirect:
32
32
  redirect = redirect.redirect()
33
33
  return Response({"redirect": redirect.url, "status": redirect.status_code})
34
- node = get_object_or_404(UrlNode, permalink=f"/{permalink}")
35
- return Response(UrlNodeSerializer(node, context={"request": request}).data)
34
+ node: UrlNode = get_object_or_404(UrlNode, permalink=f"/{permalink}")
35
+ return Response(RouteSerializer(node, context={"request": request}).data)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-camomilla-cms
3
- Version: 6.0.1
3
+ Version: 6.1.1
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=Chd2aJ9tllc6G4Js9yDbwgQf9Z4QYydv95s0ijUTuIc,243
1
+ camomilla/__init__.py,sha256=2uWlUsuuK2sHK3-Pyn8YBZqA67UaOv1gQQl3Kkqx0Eg,243
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,10 +9,10 @@ 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=yyQFUHAIyzqemWFbwvBCUPGQuB7KmhmnH_Mu9J20SQI,4116
12
+ camomilla/settings.py,sha256=lEQPysEJZ_0c9Bkr1P38rO2csPrcNCdqeLqGp1aMS2I,4362
13
13
  camomilla/sitemap.py,sha256=U2t5TwhB_-sEscmQZ69PZ5st3bIap8NRxzWEvCgB130,786
14
14
  camomilla/translation.py,sha256=_QyfTlKG6hQ_ClRfxzeJ-3oI3Nu5peJN9xFkO9Ib3As,1316
15
- camomilla/urls.py,sha256=XgaeFoG2eXlJQve3KmFKlD-74CMLW1ziaY1mq-lrAiA,2095
15
+ camomilla/urls.py,sha256=umWlVDJ_J4aPkqeSbHJznrGJi0marvWi8TfSmsRT5a0,2101
16
16
  camomilla/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
17
  camomilla/contrib/modeltranslation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
18
  camomilla/contrib/modeltranslation/hvad_migration.py,sha256=3j_q_Q85eF4iHbU4LG1Zr3LOmfmGmFiVSL-C8KvPsJQ,5409
@@ -27,8 +27,8 @@ camomilla/models/__init__.py,sha256=y7Q34AGhQ1weJUKWb6XjiyUbwRnJeylOBGMErU0wqYg,
27
27
  camomilla/models/article.py,sha256=LgkZgRsubtDV6NwBz8E2bIgKD6H3I-1QLAxEan5TYYs,1139
28
28
  camomilla/models/content.py,sha256=mIgtifb_WMIt58we5u6qWZemHvuDN1zZaBeCyzHL78A,956
29
29
  camomilla/models/media.py,sha256=pD-qldiHDOOHgux4lsivQLBcOJJrRx3a4Bg8ODNx7r0,6852
30
- camomilla/models/menu.py,sha256=hUszPcn1prWCDhk4RPvbITmyhsB2CjFkaerx9t1GWnc,3766
31
- camomilla/models/page.py,sha256=czbiKiEje4mj229_XsLMv1mOr7JzbnAD-MIMvSb9tF0,19132
30
+ camomilla/models/menu.py,sha256=OsoYKxY-08M3LjXxsnR47IuEI-DPf2IS6mi2ZWWCm9Y,3780
31
+ camomilla/models/page.py,sha256=ihxY4r_yKL_yE7DJM-U-BDuHzukMmXc2Bk8hTuVrwoA,20007
32
32
  camomilla/models/mixins/__init__.py,sha256=c2NixqvrIX4E9WGRqQbylXlqBWDXEqN9mzs_dpB0hFQ,1248
33
33
  camomilla/openapi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
34
  camomilla/openapi/schema.py,sha256=C22dhKjaJ2DTK4KWFjyMJXiwe8NLy7ZTW5d-I1dqZ7g,2546
@@ -37,7 +37,7 @@ camomilla/serializers/article.py,sha256=pYVcS0KztzjzSqgruElQMMEZcqTzmQUqXrdv_Sx5
37
37
  camomilla/serializers/content_type.py,sha256=qB2wkmkvQI6LHxfSI6auEh6M9cJRFBaHnpmkBCCzeYo,557
38
38
  camomilla/serializers/media.py,sha256=H4JVpRVxXVmn_BiqrjihKXpfLm9fLmHDFIICRDGJU4s,1940
39
39
  camomilla/serializers/menu.py,sha256=TdoyXs40PqxNevnRbBbYOOX9rUv9zQGiHFNduspaZnw,552
40
- camomilla/serializers/page.py,sha256=NNjEypVYu_9iKqdHV_-61ea37gxiHlDP5gsloV_i6yg,1834
40
+ camomilla/serializers/page.py,sha256=n2gfRkKXiQuCHEIGnJ2gC1sFsFitBxZ6WZ3bxpr_C5Q,1885
41
41
  camomilla/serializers/user.py,sha256=CzrHiVRvYYWNE4eNpCNKtJB7DjVqHHwIcP4NUBXMHSo,3706
42
42
  camomilla/serializers/utils.py,sha256=XRL4CNwQDBNpX8xT7365Dw2Cyx8Kvh18GaadgjS9awk,968
43
43
  camomilla/serializers/validators.py,sha256=X2uBlh348nJjUWHPtiu9XKCD7Etsdg0811a4xHLAUzU,2103
@@ -73,7 +73,7 @@ camomilla/templatetags/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
73
73
  camomilla/templatetags/camomilla_filters.py,sha256=35x0-cWrRHeLhqypSLlJzEFY_fQDcRHiwZQpFgIsspE,692
74
74
  camomilla/templatetags/menus.py,sha256=7fc4f9DDqtqG6wNb5_Q0km-fq0mqvGnbpR21qO1TJUw,960
75
75
  camomilla/templatetags/model_extras.py,sha256=6WfVDYP_OfuVJd3cNGNA55Wj9uWdrbfOZQ0ua9Xt_vc,2257
76
- camomilla/theme/__init__.py,sha256=unmO3xtN2S1e9meUbIjG3AZVyKxUe4HSZNXDOcpZRJg,22
76
+ camomilla/theme/__init__.py,sha256=TogwtvfScmmY5i06w1pmsYL28pcWyPETM_YMFRujQQY,22
77
77
  camomilla/theme/apps.py,sha256=Ue2H80fbFgxkQyHeU2H0fWs9Y6d-EnHYv4zz824FSRk,1066
78
78
  camomilla/theme/admin/__init__.py,sha256=TALAZaE-gWshSeGc6yy7VahdX5UfeCeoOE9Q5kJCEpM,2270
79
79
  camomilla/theme/admin/pages.py,sha256=HHi8dxjqffjMQGIRpOVNgQZhtKSSx3pFIctbqoZ9J2I,2614
@@ -97,8 +97,8 @@ camomilla/views/contents.py,sha256=JxvnmgeK8JEmCMLzVG8pVq2DwvmjXtgnIdsDnn74tA4,1
97
97
  camomilla/views/decorators.py,sha256=hR--nTGQn2mMKDrWn-0Ildzbsvp11OfoWAtedKEzmiA,982
98
98
  camomilla/views/languages.py,sha256=Rt_X7s3dbDBv4dxsQ9fnav_u0TAzzo8fGKBBx3esDsg,441
99
99
  camomilla/views/medias.py,sha256=XYa-NTLLQmSSynpfrFT3av-K_r59aRns3dTGfYMj-0Q,3002
100
- camomilla/views/menus.py,sha256=Kpygnf3tMKJ30gcblUES2NW83A37Vy75ecSGSvExGKM,3301
101
- camomilla/views/pages.py,sha256=UL74_u-18QdAkjVl74AVWZbRarEdIPrANTzdcM4iqmE,1338
100
+ camomilla/views/menus.py,sha256=2uEeQc68wmmxiQ-mEG9wj1m18uF9f4oq-_5anAVOIbw,3449
101
+ camomilla/views/pages.py,sha256=GwLRcyrLqL3g4BFcbGQ2b0yspA-osA092zTMgVys56M,1345
102
102
  camomilla/views/tags.py,sha256=XcYRlcBFSPPY32lt7POb6fWPJL_8HsTo5JcHcAOiOKw,479
103
103
  camomilla/views/users.py,sha256=_fvsKOEtep4SJLvMva2_q-HdLQT_1KlFNt4wcl3xCJk,3130
104
104
  camomilla/views/base/__init__.py,sha256=bpbVBGXLTy7No95XyDNB2U8hVXmwQJrF1VjLAS5WH90,1232
@@ -109,7 +109,7 @@ camomilla/views/mixins/optimize.py,sha256=iRPNkoeIIlJugk7DjJhDPaqeX7Opi7TxnUoMDn
109
109
  camomilla/views/mixins/ordering.py,sha256=mh7fqPyVCVJh84Nl2pYFQouzGxa-ANF3Wqv0pCb7OVU,4779
110
110
  camomilla/views/mixins/pagination.py,sha256=Ssa5mMgAgyl7sUk0yum2uVifg6esdGmUTqMSJYacE40,5772
111
111
  camomilla/views/mixins/permissions.py,sha256=TPmR3Hoa3BjeJu9rCE_7lpLOAupue4WI42C21HTo6X4,200
112
- django_camomilla_cms-6.0.1.dist-info/licenses/LICENSE,sha256=kVS7zDrNkav2hLLXbOJwVdonY2ToApTK3khyJagGQoQ,1063
112
+ django_camomilla_cms-6.1.1.dist-info/licenses/LICENSE,sha256=kVS7zDrNkav2hLLXbOJwVdonY2ToApTK3khyJagGQoQ,1063
113
113
  tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
114
114
  tests/test_admin_page_form.py,sha256=DRJxekEKeYMCf9q62ax7iBeJGhEP2NZ8o02krZBR_DA,1971
115
115
  tests/test_api.py,sha256=t03EFDezGgm4UJl8RIVvnTUkAGTB6ptm0G2lHBQ7ljc,1833
@@ -120,7 +120,9 @@ tests/test_model_api.py,sha256=ml3OlLuBfcnr2EMjwQLvVDPT2adSQ7WS4IxxXKD1InU,4121
120
120
  tests/test_model_api_permissions.py,sha256=lUlcYAOasFFQjMFnqhGXlXriCH-f6xdYSCn1Gqf1eSU,1838
121
121
  tests/test_model_api_register.py,sha256=txKaVTGt-DGrmI-6xcUEluPd7ArNi80VvlqBVXdH8zk,13555
122
122
  tests/test_models.py,sha256=WJs8lxWZWn1l7X3a_QFVc8fF5LHTsI8bc3uhQe6-o-Q,684
123
- tests/test_pages.py,sha256=aXKI0l12lc6jmY3uJXDNwznVNH7SEIiujANvSo_rhIo,11911
123
+ tests/test_page_meta.py,sha256=QFmX97LBYSuHn9vJPM80MWZc3BwfGO5V-sCClk7ExOA,3142
124
+ tests/test_page_relation_api.py,sha256=PrFY3vuuFal4og8MUq8ba02qITsNOtIP1eNmQ_f9jxk,2760
125
+ tests/test_pages.py,sha256=Zo1dV6Ie2hPPfc2V7uPbVW9gEtuBWOGypglaVIbTG98,11885
124
126
  tests/test_query_parser.py,sha256=R9l0L2QDEDcm2b6IFUhyf7wMXLzL9RySLkzKTWRtBkE,2097
125
127
  tests/test_templates_context.py,sha256=zGdmbQMGNXB2V_15BaQDIgqFMnVjBAw969n1tu3m7HY,5626
126
128
  tests/test_utils.py,sha256=ow4csGfU5WzMgAT5zWjZIxZwW1-BqnMduDt8hOzf9cE,2166
@@ -128,7 +130,7 @@ tests/fixtures/__init__.py,sha256=ixyA6ZsmYbiKEsjQGOGoG4KyJmwWrf-qeoQjQG3J66U,42
128
130
  tests/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
129
131
  tests/utils/api.py,sha256=TYcDXeILHtBwzwG0acwPFmiqMZnlF9VnLB0Ydhg55vA,865
130
132
  tests/utils/media.py,sha256=-cnrQzzVuhNSb5rT5xMUs5f3yYpBnS0fVGDcjgsb8lw,291
131
- django_camomilla_cms-6.0.1.dist-info/METADATA,sha256=JrWJpDNYLZJEirL-izKANrA95xxLSL6go3mHCmeHIMA,5652
132
- django_camomilla_cms-6.0.1.dist-info/WHEEL,sha256=JNWh1Fm1UdwIQV075glCn4MVuCRs0sotJIq-J6rbxCU,109
133
- django_camomilla_cms-6.0.1.dist-info/top_level.txt,sha256=G9VIGBmMMqC7JEckoTgXKmC6T2BR75QRkqRnngw1_lo,16
134
- django_camomilla_cms-6.0.1.dist-info/RECORD,,
133
+ django_camomilla_cms-6.1.1.dist-info/METADATA,sha256=euaGysT3yY1GYIMKgQHbVFjEYHZ3it2zD_GZDdvGwOA,5652
134
+ django_camomilla_cms-6.1.1.dist-info/WHEEL,sha256=JNWh1Fm1UdwIQV075glCn4MVuCRs0sotJIq-J6rbxCU,109
135
+ django_camomilla_cms-6.1.1.dist-info/top_level.txt,sha256=G9VIGBmMMqC7JEckoTgXKmC6T2BR75QRkqRnngw1_lo,16
136
+ django_camomilla_cms-6.1.1.dist-info/RECORD,,
@@ -0,0 +1,88 @@
1
+ import pytest
2
+ from django.test import TestCase
3
+ from rest_framework.test import APIClient
4
+
5
+ from tests.utils.media import load_asset_and_remove_media
6
+ from .utils.api import login_superuser
7
+ from camomilla.models import Page, Media
8
+ from example.website.models import CustomPageMetaModel, InvalidPageMetaModel
9
+
10
+
11
+ class PagaMetaTestCase(TestCase):
12
+ def setUp(self):
13
+ self.client = APIClient()
14
+ token = login_superuser()
15
+ self.client.credentials(HTTP_AUTHORIZATION="Token " + token)
16
+
17
+ @pytest.mark.django_db
18
+ def test_page_meta_rendering(self):
19
+ asset = load_asset_and_remove_media("37059501.png")
20
+ Media.objects.create(
21
+ file=asset,
22
+ alt_text="Test Media",
23
+ title="Test Media",
24
+ description="Description of test media",
25
+ )
26
+ page = CustomPageMetaModel.objects.create(
27
+ title="Test Page",
28
+ custom_field="Custom Data",
29
+ permalink="test-page",
30
+ status="PUB",
31
+ autopermalink=False,
32
+ )
33
+ page.save()
34
+ response = self.client.get("/test-page/")
35
+ assert response.status_code == 200
36
+ content = response.content.decode()
37
+ assert "👻 I&#x27;m beeing injected!" in content
38
+ assert "<h1>I'm the custom template!</h1>" in content
39
+ assert "<ul><li>Test Media</li></ul>" in content
40
+
41
+ @pytest.mark.django_db
42
+ def test_page_meta_custom_parent_page(self):
43
+ parent_page = Page.objects.create(
44
+ title="Parent Page",
45
+ permalink="parent-page",
46
+ status="PUB",
47
+ autopermalink=False,
48
+ )
49
+ child_page = CustomPageMetaModel.objects.create(
50
+ title="Child Page",
51
+ custom_field="Child Data",
52
+ status="PUB",
53
+ custom_parent_page=parent_page,
54
+ )
55
+ assert child_page.permalink == "/parent-page/child-page"
56
+
57
+ @pytest.mark.django_db
58
+ def test_page_meta_custom_serializer(self):
59
+
60
+ CustomPageMetaModel.objects.create(
61
+ title="Test Page with Custom Serializer",
62
+ custom_field="Custom Data",
63
+ permalink="test-page-custom-serializer",
64
+ status="PUB",
65
+ autopermalink=False,
66
+ )
67
+
68
+ response = self.client.get(
69
+ "/api/camomilla/pages-router/test-page-custom-serializer"
70
+ )
71
+ assert response.status_code == 200
72
+ data = response.json()
73
+ assert data["title"] == "Test Page with Custom Serializer"
74
+ assert data["custom_field"] == "Custom Data"
75
+ assert data["permalink"] == "/test-page-custom-serializer"
76
+ assert (
77
+ data["serializer_custom_field"]
78
+ == "I'm coming from CustomPageSerializer! 🫡"
79
+ )
80
+
81
+ @pytest.mark.django_db
82
+ def test_page_meta_custom_serializer_error(self):
83
+ with pytest.raises(ValueError) as exc_info:
84
+ InvalidPageMetaModel.get_serializer()
85
+ assert (
86
+ str(exc_info.value)
87
+ == "Standard serializer <class 'example.website.serializers.InvalidSerializer'> must be a subclass of AbstractPageMixin"
88
+ )
@@ -0,0 +1,77 @@
1
+ from django.test import TestCase
2
+ from rest_framework.test import APIClient
3
+ from .utils.api import login_superuser
4
+ from example.website.models import (
5
+ ExposedRelatedPageModel,
6
+ UnexposedRelatedPageModel,
7
+ RelatedPageModel,
8
+ )
9
+
10
+ client = APIClient()
11
+
12
+
13
+ class PageRelationApi(TestCase):
14
+ def setUp(self):
15
+ token = login_superuser()
16
+ client.credentials(HTTP_AUTHORIZATION="Token " + token)
17
+
18
+ def test_exposed_relation(self):
19
+ exposed_related_page_model = ExposedRelatedPageModel.objects.create(
20
+ title="ExposedRelatedPageModel 1",
21
+ permalink="exposed-related-page-model-1",
22
+ status="PUB",
23
+ autopermalink=False,
24
+ )
25
+ related_page_model = RelatedPageModel.objects.create(
26
+ title="RelatedPageModel 1",
27
+ permalink="related-page-model-1",
28
+ status="PUB",
29
+ autopermalink=False,
30
+ )
31
+ related_page_model.exposed_pages.add(exposed_related_page_model)
32
+ related_page_model.save()
33
+
34
+ response = client.get("/api/camomilla/pages-router/related-page-model-1")
35
+ assert response.status_code == 200
36
+ data = response.json()
37
+ assert data["exposed_pages"][0]["id"] == exposed_related_page_model.id
38
+
39
+ response = client.get(
40
+ "/api/camomilla/pages-router/exposed-related-page-model-1"
41
+ )
42
+ assert response.status_code == 200
43
+ data = response.json()
44
+ assert (
45
+ "related_pages" in data
46
+ ), "Exposed related pages should be included in the API response"
47
+
48
+ def test_unexposed_relation(self):
49
+ unexposed_related_page_model = UnexposedRelatedPageModel.objects.create(
50
+ title="UnexposedRelatedPageModel 1",
51
+ permalink="unexposed-related-page-model-1",
52
+ status="PUB",
53
+ autopermalink=False,
54
+ )
55
+ related_page_model = RelatedPageModel.objects.create(
56
+ title="RelatedPageModel 1",
57
+ permalink="related-page-model-1",
58
+ status="PUB",
59
+ autopermalink=False,
60
+ )
61
+
62
+ related_page_model.unexposed_pages.add(unexposed_related_page_model)
63
+ related_page_model.save()
64
+
65
+ response = client.get("/api/camomilla/pages-router/related-page-model-1")
66
+ assert response.status_code == 200
67
+ data = response.json()
68
+ assert data["unexposed_pages"][0]["id"] == unexposed_related_page_model.id
69
+
70
+ response = client.get(
71
+ "/api/camomilla/pages-router/unexposed-related-page-model-1"
72
+ )
73
+ assert response.status_code == 200
74
+ data = response.json()
75
+ assert (
76
+ "related_pages" not in data
77
+ ), "Unexposed related pages should not be included in the API response"
tests/test_pages.py CHANGED
@@ -165,13 +165,11 @@ class PagesTestCase(TestCase):
165
165
  # EN parent page with automatic url creation
166
166
  response = self.client.get("/api/camomilla/pages/3/?language=en")
167
167
  assert response.json()["autopermalink"] == True
168
- assert response.json()["permalink"] == "//permalink_manual_en_2/title_page_3"
168
+ assert response.json()["permalink"] == "/permalink_manual_en_2/title_page_3"
169
169
  # IT parent page with automatic url creation
170
170
  response = self.client.get("/api/camomilla/pages/3/?language=it")
171
171
  assert response.json()["autopermalink"] == True
172
- assert (
173
- response.json()["permalink"] == "//permalink_manuale_it_2/titolo_pagina_3"
174
- )
172
+ assert response.json()["permalink"] == "/permalink_manuale_it_2/titolo_pagina_3"
175
173
 
176
174
  # Check url uniqueness and consistency EN
177
175
  response = self.client.post(