django-camomilla-cms 6.0.0b11__tar.gz → 6.0.0b13__tar.gz

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 (117) hide show
  1. {django-camomilla-cms-6.0.0b11/django_camomilla_cms.egg-info → django-camomilla-cms-6.0.0b13}/PKG-INFO +1 -1
  2. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/__init__.py +1 -1
  3. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/contrib/rest_framework/serializer.py +3 -3
  4. django-camomilla-cms-6.0.0b13/camomilla/managers/__init__.py +3 -0
  5. django-camomilla-cms-6.0.0b13/camomilla/managers/pages.py +31 -0
  6. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/models/menu.py +2 -2
  7. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/models/page.py +36 -6
  8. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/serializers/fields/json.py +3 -3
  9. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/serializers/mixins/__init__.py +2 -2
  10. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/structured/__init__.py +4 -4
  11. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/structured/cache.py +3 -4
  12. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/structured/fields.py +2 -2
  13. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/templates_context/rendering.py +5 -5
  14. django-camomilla-cms-6.0.0b13/camomilla/theme/__init__.py +1 -0
  15. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13/django_camomilla_cms.egg-info}/PKG-INFO +1 -1
  16. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/django_camomilla_cms.egg-info/SOURCES.txt +2 -0
  17. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/setup.py +1 -1
  18. django-camomilla-cms-6.0.0b11/camomilla/theme/__init__.py +0 -1
  19. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/LICENSE +0 -0
  20. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/MANIFEST.in +0 -0
  21. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/README.md +0 -0
  22. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/apps.py +0 -0
  23. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/authentication.py +0 -0
  24. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/context_processors.py +0 -0
  25. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/contrib/__init__.py +0 -0
  26. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/contrib/modeltranslation/__init__.py +0 -0
  27. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/contrib/modeltranslation/hvad_migration.py +0 -0
  28. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/contrib/rest_framework/__init__.py +0 -0
  29. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/defaults.py +0 -0
  30. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/dynamic_pages_urls.py +0 -0
  31. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/exceptions.py +0 -0
  32. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/fields/__init__.py +0 -0
  33. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/fields/json.py +0 -0
  34. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/management/__init__.py +0 -0
  35. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/management/commands/__init__.py +0 -0
  36. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/management/commands/regenerate_thumbnails.py +0 -0
  37. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/model_api.py +0 -0
  38. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/models/__init__.py +0 -0
  39. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/models/article.py +0 -0
  40. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/models/content.py +0 -0
  41. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/models/media.py +0 -0
  42. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/models/mixins/__init__.py +0 -0
  43. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/openapi/__init__.py +0 -0
  44. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/openapi/schema.py +0 -0
  45. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/parsers.py +0 -0
  46. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/permissions.py +0 -0
  47. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/serializers/__init__.py +0 -0
  48. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/serializers/article.py +0 -0
  49. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/serializers/base/__init__.py +0 -0
  50. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/serializers/content_type.py +0 -0
  51. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/serializers/fields/__init__.py +0 -0
  52. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/serializers/fields/file.py +0 -0
  53. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/serializers/fields/related.py +0 -0
  54. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/serializers/media.py +0 -0
  55. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/serializers/menu.py +0 -0
  56. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/serializers/page.py +0 -0
  57. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/serializers/user.py +0 -0
  58. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/serializers/utils.py +0 -0
  59. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/serializers/validators.py +0 -0
  60. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/settings.py +0 -0
  61. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/sitemap.py +0 -0
  62. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/storages/__init__.py +0 -0
  63. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/storages/optimize.py +0 -0
  64. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/storages/overwrite.py +0 -0
  65. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/structured/models.py +0 -0
  66. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/structured/utils.py +0 -0
  67. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/templates/admin/camomilla/page/change_form.html +0 -0
  68. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/templates/defaults/articles/default.html +0 -0
  69. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/templates/defaults/base.html +0 -0
  70. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/templates/defaults/pages/default.html +0 -0
  71. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/templates/defaults/parts/langswitch.html +0 -0
  72. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/templates/defaults/parts/menu.html +0 -0
  73. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/templates/defaults/widgets/media_select_multiple.html +0 -0
  74. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/templates_context/__init__.py +0 -0
  75. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/templates_context/autodiscover.py +0 -0
  76. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/templatetags/__init__.py +0 -0
  77. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/templatetags/camomilla_filters.py +0 -0
  78. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/templatetags/menus.py +0 -0
  79. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/theme/admin.py +0 -0
  80. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/theme/apps.py +0 -0
  81. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/theme/static/admin/css/responsive.css +0 -0
  82. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/theme/static/admin/img/favicon.ico +0 -0
  83. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/theme/static/admin/img/logo.svg +0 -0
  84. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/theme/templates/admin/base.html +0 -0
  85. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/theme/templates/rosetta/base.html +0 -0
  86. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/translation.py +0 -0
  87. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/urls.py +0 -0
  88. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/utils/__init__.py +0 -0
  89. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/utils/getters.py +0 -0
  90. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/utils/normalization.py +0 -0
  91. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/utils/seo.py +0 -0
  92. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/utils/templates.py +0 -0
  93. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/utils/translation.py +0 -0
  94. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/views/__init__.py +0 -0
  95. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/views/articles.py +0 -0
  96. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/views/base/__init__.py +0 -0
  97. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/views/contents.py +0 -0
  98. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/views/decorators.py +0 -0
  99. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/views/languages.py +0 -0
  100. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/views/medias.py +0 -0
  101. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/views/menus.py +0 -0
  102. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/views/mixins/__init__.py +0 -0
  103. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/views/mixins/ordering.py +0 -0
  104. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/views/mixins/pagination.py +0 -0
  105. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/views/pages.py +0 -0
  106. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/views/tags.py +0 -0
  107. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/camomilla/views/users.py +0 -0
  108. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/django_camomilla_cms.egg-info/dependency_links.txt +0 -0
  109. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/django_camomilla_cms.egg-info/requires.txt +0 -0
  110. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/django_camomilla_cms.egg-info/top_level.txt +0 -0
  111. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/pyproject.toml +0 -0
  112. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/setup.cfg +0 -0
  113. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/tests/__init__.py +0 -0
  114. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/tests/test_api.py +0 -0
  115. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/tests/test_camomilla_filters.py +0 -0
  116. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/tests/test_models.py +0 -0
  117. {django-camomilla-cms-6.0.0b11 → django-camomilla-cms-6.0.0b13}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: django-camomilla-cms
3
- Version: 6.0.0b11
3
+ Version: 6.0.0b13
4
4
  Summary: Django powered cms
5
5
  Author-email: Lotrèk <dimmitutto@lotrek.it>
6
6
  License: MIT
@@ -1,4 +1,4 @@
1
- __version__ = "6.0.0-beta.11"
1
+ __version__ = "6.0.0-beta.13"
2
2
 
3
3
 
4
4
  def get_core_apps():
@@ -1,5 +1,5 @@
1
1
  from functools import cached_property
2
- from typing import Iterable, Union
2
+ from typing import Iterable, Union, List
3
3
  from django.http import QueryDict
4
4
 
5
5
  from modeltranslation import settings as mt_settings
@@ -33,7 +33,7 @@ def plain_to_nest(data, fields, accessor=TRANS_ACCESSOR):
33
33
  return data
34
34
 
35
35
 
36
- def nest_to_plain(data: Union[dict, QueryDict], fields: list[str], accessor=TRANS_ACCESSOR):
36
+ def nest_to_plain(data: Union[dict, QueryDict], fields: List[str], accessor=TRANS_ACCESSOR):
37
37
  """
38
38
  This function is the inverse of plain_to_nest.
39
39
  It transforms a dictionary with nested translations fields (es. {"translations": {"en": {"title": "Hello"}}})
@@ -68,7 +68,7 @@ class TranslationsMixin(serializers.ModelSerializer):
68
68
  """
69
69
 
70
70
  @cached_property
71
- def translation_fields(self) -> list[str]:
71
+ def translation_fields(self) -> List[str]:
72
72
  try:
73
73
  return translator.get_options_for_model(self.Meta.model).get_field_names()
74
74
  except NotRegistered:
@@ -0,0 +1,3 @@
1
+ from .pages import PageQuerySet
2
+
3
+ __all__ = ["PageQuerySet"]
@@ -0,0 +1,31 @@
1
+ from typing import Any, Tuple
2
+ from django.db.models.query import QuerySet
3
+ from django.db import transaction
4
+ from django.apps import apps
5
+
6
+ URL_NODE_RELATED_NAME = "%(app_label)s_%(class)s"
7
+
8
+
9
+ class PageQuerySet(QuerySet):
10
+ def get_or_create(self, defaults=None, **kwargs) -> Tuple[Any, bool]:
11
+ if "permalink" in kwargs:
12
+ with transaction.atomic():
13
+ UrlNode = apps.get_model("camomilla", "UrlNode")
14
+ url_node, created = UrlNode.objects.get_or_create(
15
+ permalink=kwargs.pop("permalink"),
16
+ related_name=URL_NODE_RELATED_NAME % self.model_info
17
+ )
18
+ if created is False and url_node.page is not None:
19
+ page = self.get(**kwargs)
20
+ if page.pk != url_node.page.pk:
21
+ raise self.model.MultipleObjectsReturned(
22
+ "got more than one %s object for the same permalink: %s"
23
+ % (
24
+ self.model._meta.object_name,
25
+ kwargs["permalink"],
26
+ )
27
+ )
28
+ return url_node.page, False
29
+ kwargs["url_node"] = url_node
30
+ return super().get_or_create(defaults, **kwargs)
31
+ return super().get_or_create(defaults, **kwargs)
@@ -14,7 +14,7 @@ from pydantic import (
14
14
  )
15
15
  from camomilla import structured
16
16
  from camomilla.models.page import UrlNode
17
- from typing import Optional, Union, Callable
17
+ from typing import Optional, Union, Callable, List
18
18
 
19
19
 
20
20
  class LinkTypes(str, Enum):
@@ -54,7 +54,7 @@ class MenuNodeLink(structured.BaseModel):
54
54
  class MenuNode(structured.BaseModel):
55
55
  id: str = Field(default_factory=uuid4)
56
56
  meta: dict = {}
57
- nodes: list["MenuNode"] = []
57
+ nodes: List["MenuNode"] = []
58
58
  title: str = ""
59
59
  link: MenuNodeLink
60
60
 
@@ -2,6 +2,8 @@ from typing import Sequence, Tuple
2
2
  from uuid import uuid4
3
3
 
4
4
  from django.core.exceptions import ObjectDoesNotExist
5
+ from django.core.validators import RegexValidator
6
+
5
7
  from django.db import ProgrammingError, OperationalError, models, transaction
6
8
  from django.db.models.signals import post_delete
7
9
  from django.dispatch import receiver
@@ -12,6 +14,7 @@ from django.utils.functional import lazy
12
14
  from django.utils.text import slugify
13
15
  from django.utils.translation import gettext_lazy as _
14
16
 
17
+ from camomilla.managers.pages import PageQuerySet
15
18
  from camomilla.models.mixins import MetaMixin, SeoMixin
16
19
  from camomilla.utils import (
17
20
  activate_languages,
@@ -164,6 +167,16 @@ class PageBase(models.base.ModelBase):
164
167
  return new_class
165
168
 
166
169
 
170
+ class UrlPathValidator(RegexValidator):
171
+
172
+ regex = r"^[a-zA-Z0-9_\-\/]+[^\/]$"
173
+ message = _(
174
+ "Enter a valid 'slug' consisting of lowercase letters, numbers, "
175
+ "underscores, hyphens and slashes."
176
+ )
177
+ flags = 0
178
+
179
+
167
180
  class AbstractPage(SeoMixin, MetaMixin, models.Model, metaclass=PageBase):
168
181
  date_created = models.DateTimeField(auto_now_add=True)
169
182
  date_updated_at = models.DateTimeField(auto_now=True)
@@ -175,7 +188,9 @@ class AbstractPage(SeoMixin, MetaMixin, models.Model, metaclass=PageBase):
175
188
  editable=False,
176
189
  )
177
190
  breadcrumbs_title = models.CharField(max_length=128, null=True, blank=True)
178
- slug = models.SlugField(max_length=150, allow_unicode=True, null=True, blank=True)
191
+ slug = models.CharField(
192
+ max_length=150, null=True, blank=True, validators=[UrlPathValidator()]
193
+ )
179
194
  status = models.CharField(
180
195
  max_length=3,
181
196
  choices=PAGE_STATUS,
@@ -195,6 +210,8 @@ class AbstractPage(SeoMixin, MetaMixin, models.Model, metaclass=PageBase):
195
210
  on_delete=models.CASCADE,
196
211
  )
197
212
 
213
+ objects = PageQuerySet.as_manager()
214
+
198
215
  def __init__(self, *args, **kwargs):
199
216
  super(AbstractPage, self).__init__(*args, **kwargs)
200
217
  self._meta.get_field("template").choices = lazy(GET_TEMPLATE_CHOICES, list)()
@@ -294,7 +311,10 @@ class AbstractPage(SeoMixin, MetaMixin, models.Model, metaclass=PageBase):
294
311
  else fallback_slug
295
312
  )
296
313
  set_nofallbacks(self, "slug", slug)
297
- permalink = "/%s" % slugify(slug or "", allow_unicode=True)
314
+ slug_parts = (slug or "").split("/")
315
+ for i, part in enumerate(slug_parts):
316
+ slug_parts[i] = slugify(part, allow_unicode=True)
317
+ permalink = "/%s" % "/".join(slug_parts)
298
318
  if self.parent:
299
319
  permalink = f"{self.parent.permalink}{permalink}"
300
320
  qs = UrlNode.objects.exclude(pk=getattr(self.url_node or object, "pk", None))
@@ -319,12 +339,14 @@ class AbstractPage(SeoMixin, MetaMixin, models.Model, metaclass=PageBase):
319
339
  def get(cls, request, *args, **kwargs) -> "AbstractPage":
320
340
  bypass_type_check = kwargs.pop("bypass_type_check", False)
321
341
  bypass_public_check = kwargs.pop("bypass_public_check", False)
322
- path = request.path
323
- if getattr(django_settings, "APPEND_SLASH", True):
324
- path = path.rstrip("/")
325
342
  if len(kwargs.keys()) > 0:
326
343
  page = cls.objects.get(**kwargs)
327
344
  else:
345
+ if not request:
346
+ raise ValueError("request is required if no kwargs are passed")
347
+ path = request.path
348
+ if getattr(django_settings, "APPEND_SLASH", True):
349
+ path = path.rstrip("/")
328
350
  node = UrlNode.objects.filter(
329
351
  permalink=url_lang_decompose(path)["permalink"]
330
352
  ).first()
@@ -374,12 +396,20 @@ class AbstractPage(SeoMixin, MetaMixin, models.Model, metaclass=PageBase):
374
396
  raise Http404(ex)
375
397
 
376
398
  def alternate_urls(self, *args, **kwargs) -> dict:
399
+ request = False
400
+ if len(args) > 0:
401
+ request = args[0]
402
+ if "request" in kwargs:
403
+ request = kwargs["request"]
404
+ preview = request and getattr(request, "GET", {}).get("preview", False)
377
405
  permalinks = get_field_translations(self.url_node or object, "permalink", None)
378
406
  for lang in activate_languages():
379
407
  if lang in permalinks:
380
408
  permalinks[lang] = (
381
- UrlNode.reverse_url(permalinks[lang]) if self.is_public else None
409
+ UrlNode.reverse_url(permalinks[lang]) if preview or self.is_public else None
382
410
  )
411
+ if preview:
412
+ permalinks = {k: f"{v}?preview=true" for k, v in permalinks.items()}
383
413
  return permalinks
384
414
 
385
415
  class Meta:
@@ -1,7 +1,7 @@
1
1
  from pydantic import TypeAdapter, ValidationError
2
2
  from rest_framework import serializers
3
3
  from rest_framework.utils import model_meta
4
- from typing import TYPE_CHECKING, Any, Union
4
+ from typing import TYPE_CHECKING, Any, Union, Dict, List
5
5
 
6
6
  from camomilla.structured.utils import pointed_setter
7
7
 
@@ -29,7 +29,7 @@ class StructuredJSONField(serializers.JSONField):
29
29
  self.json_schema = field.schema.json_schema()
30
30
  super().bind(field_name, parent)
31
31
 
32
- def to_representation(self, instance: Union["BaseModel", list["BaseModel"]]):
32
+ def to_representation(self, instance: Union["BaseModel", List["BaseModel"]]):
33
33
  if isinstance(instance, list) and self.many:
34
34
  return super().to_representation(
35
35
  self.schema.dump_python(instance, exclude_unset=True)
@@ -40,7 +40,7 @@ class StructuredJSONField(serializers.JSONField):
40
40
  try:
41
41
  return self.schema.validate_python(super().to_internal_value(data))
42
42
  except ValidationError as e:
43
- drf_error: Union[list, dict[str, Any]] = [] if self.many else {}
43
+ drf_error: Union[list, Dict[str, Any]] = [] if self.many else {}
44
44
  for error in e.errors():
45
45
  pointed_setter(
46
46
  drf_error, ".".join([str(x) for x in error["loc"]]), [error["msg"]]
@@ -161,9 +161,9 @@ class AbstractPageMixin(serializers.ModelSerializer):
161
161
 
162
162
  breadcrumbs = serializers.SerializerMethodField()
163
163
  routerlink = serializers.CharField(read_only=True)
164
- template = serializers.SerializerMethodField()
164
+ template_file = serializers.SerializerMethodField()
165
165
 
166
- def get_template(self, instance: "AbstractPage"):
166
+ def get_template_file(self, instance: "AbstractPage"):
167
167
  return instance.get_template_path()
168
168
 
169
169
  def get_breadcrumbs(self, instance: "AbstractPage"):
@@ -1,5 +1,5 @@
1
1
  import json
2
- from typing import Any, Union, TYPE_CHECKING
2
+ from typing import Any, Union, Type, TYPE_CHECKING, List
3
3
 
4
4
  from django.db.models import JSONField
5
5
  from django.db.models.query_utils import DeferredAttribute
@@ -70,7 +70,7 @@ class StructuredJSONField(JSONField):
70
70
 
71
71
  return list_data_validator
72
72
 
73
- def __init__(self, schema: type[BaseModel], *args: Any, **kwargs: Any) -> None:
73
+ def __init__(self, schema: Type[BaseModel], *args: Any, **kwargs: Any) -> None:
74
74
  self.orig_schema = schema
75
75
  self.schema = schema
76
76
  default = kwargs.get("default", dict)
@@ -81,7 +81,7 @@ class StructuredJSONField(JSONField):
81
81
  if self.many:
82
82
  self.schema = TypeAdapter(
83
83
  Annotated[
84
- list[self.schema],
84
+ List[self.schema],
85
85
  Field(default_factory=list),
86
86
  WrapValidator(self.list_data_validator),
87
87
  ]
@@ -96,7 +96,7 @@ class StructuredJSONField(JSONField):
96
96
  return isinstance(value, self.orig_schema)
97
97
 
98
98
  def get_prep_value(
99
- self, value: Union[list[type[BaseModel]], type[BaseModel]]
99
+ self, value: Union[List[Type[BaseModel]], Type[BaseModel]]
100
100
  ) -> str:
101
101
  if isinstance(value, list) and self.many:
102
102
  return self.schema.dump_json(value, exclude_unset=True).decode()
@@ -1,6 +1,6 @@
1
1
  from collections import defaultdict
2
2
  from inspect import isclass
3
- from typing import Any, Dict, Sequence
3
+ from typing import Any, Dict, Sequence, Type, Iterable, Union
4
4
  from typing_extensions import get_args, get_origin
5
5
  from camomilla.settings import STRUCTURED_FIELD_CACHE_ENABLED
6
6
  from camomilla.structured.fields import ForeignKey, QuerySet
@@ -8,7 +8,6 @@ from camomilla.structured.models import BaseModel
8
8
  from camomilla.structured.utils import _LazyType, get_type, pointed_setter
9
9
  from django.db.models import Model as DjangoModel
10
10
  from camomilla.utils.getters import pointed_getter
11
- from typing import Iterable, Union
12
11
 
13
12
 
14
13
  # TODO:
@@ -26,7 +25,7 @@ class ValueWithCache:
26
25
  def __init__(self, cache, model, value) -> None:
27
26
  self.cache: Cache = cache
28
27
  self.value: Union[Iterable[Union[str, int]], str, int] = value
29
- self.model: type[DjangoModel] = model
28
+ self.model: Type[DjangoModel] = model
30
29
 
31
30
  def retrieve(self):
32
31
  cache = self.cache.get(self.model)
@@ -67,7 +66,7 @@ class CacheBuilder:
67
66
  return cls(related_fields=cls.inspect_related_fields(model))
68
67
 
69
68
  @classmethod
70
- def inspect_related_fields(cls, model: type[BaseModel]) -> Dict[str, RelInfo]:
69
+ def inspect_related_fields(cls, model: Type[BaseModel]) -> Dict[str, RelInfo]:
71
70
  related = {}
72
71
  for field_name, field in model.model_fields.items():
73
72
  annotation = field.annotation
@@ -84,7 +84,7 @@ class QuerySet(Generic[T]):
84
84
  model_class = get_type(source)
85
85
 
86
86
  def validate_from_pk_list(
87
- values: list[Union[int, str]]
87
+ values: List[Union[int, str]]
88
88
  ) -> django_models.QuerySet:
89
89
  preserved = django_models.Case(
90
90
  *[django_models.When(pk=pk, then=pos) for pos, pk in enumerate(values)]
@@ -103,7 +103,7 @@ class QuerySet(Generic[T]):
103
103
  pk_attname = model_class._meta.pk.attname
104
104
 
105
105
  def validate_from_dict(
106
- values: list[Dict[str, Union[str, int]]]
106
+ values: List[Dict[str, Union[str, int]]]
107
107
  ) -> django_models.QuerySet:
108
108
  return validate_from_pk_list([data[pk_attname] for data in values])
109
109
 
@@ -1,5 +1,5 @@
1
1
  from collections import defaultdict
2
- from typing import Callable, Optional, TYPE_CHECKING
2
+ from typing import Callable, Optional, TYPE_CHECKING, Type, Dict
3
3
  import inspect
4
4
  from django.http import HttpRequest
5
5
 
@@ -16,7 +16,7 @@ class PagesContextRegistry:
16
16
  self,
17
17
  func: Callable,
18
18
  template_path: Optional[str],
19
- page_model: Optional[type["AbstractPage"]],
19
+ page_model: Optional[Type["AbstractPage"]],
20
20
  ):
21
21
  assert (
22
22
  template_path is not None or page_model is not None
@@ -26,7 +26,7 @@ class PagesContextRegistry:
26
26
  if page_model is not None:
27
27
  self._model_registry[page_model].add(func)
28
28
 
29
- def get_registered_info(self) -> dict[str, dict]:
29
+ def get_registered_info(self) -> Dict[str, dict]:
30
30
  info = defaultdict(dict)
31
31
  for k, v in self._template_registry.items():
32
32
  for f in v:
@@ -43,7 +43,7 @@ class PagesContextRegistry:
43
43
  return info
44
44
 
45
45
  def get_wrapper_context_func(
46
- self, template_path: str, page_model: type["AbstractPage"]
46
+ self, template_path: str, page_model: Type["AbstractPage"]
47
47
  ) -> Callable:
48
48
  all_funcs = set()
49
49
  for cls in self._model_registry.keys():
@@ -73,7 +73,7 @@ class PagesContextRegistry:
73
73
 
74
74
  def register(
75
75
  template_path: Optional[str] = None,
76
- page_model: Optional[type["AbstractPage"]] = None,
76
+ page_model: Optional[Type["AbstractPage"]] = None,
77
77
  ):
78
78
  assert (
79
79
  template_path is not None or page_model is not None
@@ -0,0 +1 @@
1
+ __version__ = "6.0.0-beta.13"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: django-camomilla-cms
3
- Version: 6.0.0b11
3
+ Version: 6.0.0b13
4
4
  Summary: Django powered cms
5
5
  Author-email: Lotrèk <dimmitutto@lotrek.it>
6
6
  License: MIT
@@ -27,6 +27,8 @@ camomilla/fields/json.py
27
27
  camomilla/management/__init__.py
28
28
  camomilla/management/commands/__init__.py
29
29
  camomilla/management/commands/regenerate_thumbnails.py
30
+ camomilla/managers/__init__.py
31
+ camomilla/managers/pages.py
30
32
  camomilla/models/__init__.py
31
33
  camomilla/models/article.py
32
34
  camomilla/models/content.py
@@ -1,5 +1,5 @@
1
1
  from setuptools import setup
2
2
 
3
- __version__ = "6.0.0-beta.11"
3
+ __version__ = "6.0.0-beta.13"
4
4
 
5
5
  setup(version=__version__)
@@ -1 +0,0 @@
1
- __version__ = "6.0.0-beta.11"