django-camomilla-cms 5.8.5__tar.gz → 6.0.0__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 (184) hide show
  1. django_camomilla_cms-6.0.0/PKG-INFO +123 -0
  2. django_camomilla_cms-6.0.0/README.md +94 -0
  3. django_camomilla_cms-6.0.0/camomilla/__init__.py +11 -0
  4. django_camomilla_cms-6.0.0/camomilla/apps.py +16 -0
  5. django_camomilla_cms-6.0.0/camomilla/context_processors.py +6 -0
  6. django_camomilla_cms-6.0.0/camomilla/contrib/modeltranslation/hvad_migration.py +126 -0
  7. django_camomilla_cms-6.0.0/camomilla/dynamic_pages_urls.py +33 -0
  8. django_camomilla_cms-6.0.0/camomilla/fields/__init__.py +13 -0
  9. django-camomilla-cms-5.8.5/camomilla/fields.py → django_camomilla_cms-6.0.0/camomilla/fields/json.py +15 -18
  10. {django-camomilla-cms-5.8.5 → django_camomilla_cms-6.0.0}/camomilla/management/commands/regenerate_thumbnails.py +0 -1
  11. django_camomilla_cms-6.0.0/camomilla/managers/__init__.py +3 -0
  12. django_camomilla_cms-6.0.0/camomilla/managers/pages.py +116 -0
  13. django_camomilla_cms-6.0.0/camomilla/model_api.py +86 -0
  14. django_camomilla_cms-6.0.0/camomilla/models/__init__.py +5 -0
  15. django_camomilla_cms-6.0.0/camomilla/models/article.py +44 -0
  16. {django-camomilla-cms-5.8.5 → django_camomilla_cms-6.0.0}/camomilla/models/content.py +8 -15
  17. django_camomilla_cms-6.0.0/camomilla/models/media.py +209 -0
  18. django_camomilla_cms-6.0.0/camomilla/models/menu.py +106 -0
  19. django_camomilla_cms-6.0.0/camomilla/models/mixins/__init__.py +39 -0
  20. django_camomilla_cms-6.0.0/camomilla/models/page.py +533 -0
  21. django_camomilla_cms-6.0.0/camomilla/openapi/schema.py +67 -0
  22. {django-camomilla-cms-5.8.5 → django_camomilla_cms-6.0.0}/camomilla/parsers.py +0 -1
  23. django_camomilla_cms-6.0.0/camomilla/redirects.py +10 -0
  24. {django-camomilla-cms-5.8.5 → django_camomilla_cms-6.0.0}/camomilla/serializers/__init__.py +2 -0
  25. django_camomilla_cms-6.0.0/camomilla/serializers/article.py +15 -0
  26. django_camomilla_cms-6.0.0/camomilla/serializers/base/__init__.py +39 -0
  27. django_camomilla_cms-6.0.0/camomilla/serializers/content_type.py +17 -0
  28. django_camomilla_cms-6.0.0/camomilla/serializers/fields/__init__.py +9 -0
  29. {django-camomilla-cms-5.8.5 → django_camomilla_cms-6.0.0}/camomilla/serializers/fields/file.py +5 -0
  30. {django-camomilla-cms-5.8.5 → django_camomilla_cms-6.0.0}/camomilla/serializers/fields/related.py +24 -4
  31. {django-camomilla-cms-5.8.5 → django_camomilla_cms-6.0.0}/camomilla/serializers/media.py +6 -8
  32. django_camomilla_cms-6.0.0/camomilla/serializers/menu.py +17 -0
  33. django_camomilla_cms-6.0.0/camomilla/serializers/mixins/__init__.py +23 -0
  34. django_camomilla_cms-6.0.0/camomilla/serializers/mixins/fields.py +20 -0
  35. django_camomilla_cms-6.0.0/camomilla/serializers/mixins/filter_fields.py +57 -0
  36. django_camomilla_cms-6.0.0/camomilla/serializers/mixins/json.py +34 -0
  37. django_camomilla_cms-6.0.0/camomilla/serializers/mixins/language.py +32 -0
  38. django_camomilla_cms-6.0.0/camomilla/serializers/mixins/nesting.py +35 -0
  39. django_camomilla_cms-6.0.0/camomilla/serializers/mixins/optimize.py +91 -0
  40. django_camomilla_cms-6.0.0/camomilla/serializers/mixins/ordering.py +34 -0
  41. django_camomilla_cms-6.0.0/camomilla/serializers/mixins/page.py +58 -0
  42. django_camomilla_cms-6.0.0/camomilla/serializers/mixins/translation.py +103 -0
  43. django_camomilla_cms-6.0.0/camomilla/serializers/page.py +63 -0
  44. {django-camomilla-cms-5.8.5 → django_camomilla_cms-6.0.0}/camomilla/serializers/user.py +5 -4
  45. django_camomilla_cms-6.0.0/camomilla/serializers/utils.py +38 -0
  46. django_camomilla_cms-6.0.0/camomilla/serializers/validators.py +51 -0
  47. django_camomilla_cms-6.0.0/camomilla/settings.py +118 -0
  48. django_camomilla_cms-6.0.0/camomilla/sitemap.py +30 -0
  49. django_camomilla_cms-6.0.0/camomilla/storages/__init__.py +4 -0
  50. django_camomilla_cms-6.0.0/camomilla/storages/default.py +12 -0
  51. django_camomilla_cms-6.0.0/camomilla/storages/optimize.py +71 -0
  52. django-camomilla-cms-5.8.5/camomilla/storages.py → django_camomilla_cms-6.0.0/camomilla/storages/overwrite.py +2 -2
  53. django_camomilla_cms-6.0.0/camomilla/templates/admin/camomilla/page/change_form.html +10 -0
  54. django_camomilla_cms-6.0.0/camomilla/templates/defaults/articles/default.html +7 -0
  55. django_camomilla_cms-6.0.0/camomilla/templates/defaults/base.html +170 -0
  56. django_camomilla_cms-6.0.0/camomilla/templates/defaults/pages/default.html +3 -0
  57. django_camomilla_cms-6.0.0/camomilla/templates/defaults/parts/langswitch.html +83 -0
  58. django_camomilla_cms-6.0.0/camomilla/templates/defaults/parts/menu.html +15 -0
  59. django_camomilla_cms-6.0.0/camomilla/templates_context/__init__.py +0 -0
  60. django_camomilla_cms-6.0.0/camomilla/templates_context/autodiscover.py +51 -0
  61. django_camomilla_cms-6.0.0/camomilla/templates_context/rendering.py +89 -0
  62. django_camomilla_cms-6.0.0/camomilla/templatetags/__init__.py +0 -0
  63. {django-camomilla-cms-5.8.5 → django_camomilla_cms-6.0.0}/camomilla/templatetags/camomilla_filters.py +6 -5
  64. django_camomilla_cms-6.0.0/camomilla/templatetags/menus.py +37 -0
  65. django_camomilla_cms-6.0.0/camomilla/templatetags/model_extras.py +77 -0
  66. django_camomilla_cms-6.0.0/camomilla/theme/__init__.py +1 -0
  67. django_camomilla_cms-6.0.0/camomilla/theme/admin/__init__.py +99 -0
  68. django_camomilla_cms-6.0.0/camomilla/theme/admin/pages.py +46 -0
  69. django_camomilla_cms-6.0.0/camomilla/theme/admin/translations.py +13 -0
  70. django_camomilla_cms-6.0.0/camomilla/theme/apps.py +38 -0
  71. django_camomilla_cms-6.0.0/camomilla/theme/static/admin/css/responsive.css +7 -0
  72. django_camomilla_cms-6.0.0/camomilla/theme/static/admin/img/favicon.ico +0 -0
  73. django_camomilla_cms-6.0.0/camomilla/theme/static/admin/img/logo.svg +31 -0
  74. django_camomilla_cms-6.0.0/camomilla/theme/templates/admin/base.html +7 -0
  75. django_camomilla_cms-6.0.0/camomilla/theme/templates/rosetta/base.html +196 -0
  76. django_camomilla_cms-6.0.0/camomilla/translation.py +61 -0
  77. {django-camomilla-cms-5.8.5 → django_camomilla_cms-6.0.0}/camomilla/urls.py +38 -17
  78. django_camomilla_cms-6.0.0/camomilla/utils/__init__.py +4 -0
  79. django_camomilla_cms-6.0.0/camomilla/utils/getters.py +27 -0
  80. django_camomilla_cms-6.0.0/camomilla/utils/normalization.py +7 -0
  81. django_camomilla_cms-6.0.0/camomilla/utils/query_parser.py +167 -0
  82. django-camomilla-cms-5.8.5/camomilla/utils.py → django_camomilla_cms-6.0.0/camomilla/utils/seo.py +13 -15
  83. django_camomilla_cms-6.0.0/camomilla/utils/setters.py +37 -0
  84. django_camomilla_cms-6.0.0/camomilla/utils/templates.py +32 -0
  85. django_camomilla_cms-6.0.0/camomilla/utils/translation.py +114 -0
  86. {django-camomilla-cms-5.8.5 → django_camomilla_cms-6.0.0}/camomilla/views/__init__.py +1 -1
  87. {django-camomilla-cms-5.8.5 → django_camomilla_cms-6.0.0}/camomilla/views/articles.py +5 -7
  88. django_camomilla_cms-6.0.0/camomilla/views/base/__init__.py +38 -0
  89. {django-camomilla-cms-5.8.5 → django_camomilla_cms-6.0.0}/camomilla/views/contents.py +6 -11
  90. django_camomilla_cms-6.0.0/camomilla/views/decorators.py +26 -0
  91. {django-camomilla-cms-5.8.5 → django_camomilla_cms-6.0.0}/camomilla/views/medias.py +24 -19
  92. django_camomilla_cms-6.0.0/camomilla/views/menus.py +81 -0
  93. django_camomilla_cms-6.0.0/camomilla/views/mixins/__init__.py +17 -0
  94. django_camomilla_cms-6.0.0/camomilla/views/mixins/bulk_actions.py +22 -0
  95. django_camomilla_cms-6.0.0/camomilla/views/mixins/language.py +33 -0
  96. django_camomilla_cms-6.0.0/camomilla/views/mixins/optimize.py +18 -0
  97. {django-camomilla-cms-5.8.5 → django_camomilla_cms-6.0.0}/camomilla/views/mixins/ordering.py +2 -2
  98. {django-camomilla-cms-5.8.5 → django_camomilla_cms-6.0.0}/camomilla/views/mixins/pagination.py +12 -18
  99. django_camomilla_cms-6.0.0/camomilla/views/mixins/permissions.py +6 -0
  100. django_camomilla_cms-6.0.0/camomilla/views/pages.py +35 -0
  101. django_camomilla_cms-6.0.0/camomilla/views/tags.py +12 -0
  102. {django-camomilla-cms-5.8.5 → django_camomilla_cms-6.0.0}/camomilla/views/users.py +7 -12
  103. django_camomilla_cms-6.0.0/django_camomilla_cms.egg-info/PKG-INFO +123 -0
  104. django_camomilla_cms-6.0.0/django_camomilla_cms.egg-info/SOURCES.txt +138 -0
  105. django_camomilla_cms-6.0.0/django_camomilla_cms.egg-info/requires.txt +11 -0
  106. django_camomilla_cms-6.0.0/pyproject.toml +60 -0
  107. django_camomilla_cms-6.0.0/setup.cfg +4 -0
  108. {django-camomilla-cms-5.8.5 → django_camomilla_cms-6.0.0}/setup.py +1 -1
  109. django_camomilla_cms-6.0.0/tests/__init__.py +0 -0
  110. django_camomilla_cms-6.0.0/tests/fixtures/__init__.py +14 -0
  111. django_camomilla_cms-6.0.0/tests/test_api.py +59 -0
  112. {django-camomilla-cms-5.8.5 → django_camomilla_cms-6.0.0}/tests/test_camomilla_filters.py +11 -13
  113. django_camomilla_cms-6.0.0/tests/test_media.py +152 -0
  114. django_camomilla_cms-6.0.0/tests/test_menu.py +112 -0
  115. django_camomilla_cms-6.0.0/tests/test_model_api.py +113 -0
  116. django_camomilla_cms-6.0.0/tests/test_model_api_permissions.py +44 -0
  117. django_camomilla_cms-6.0.0/tests/test_model_api_register.py +355 -0
  118. django_camomilla_cms-6.0.0/tests/test_pages.py +351 -0
  119. django_camomilla_cms-6.0.0/tests/test_query_parser.py +58 -0
  120. django_camomilla_cms-6.0.0/tests/test_templates_context.py +149 -0
  121. django_camomilla_cms-6.0.0/tests/test_utils.py +86 -0
  122. django_camomilla_cms-6.0.0/tests/utils/__init__.py +0 -0
  123. django_camomilla_cms-6.0.0/tests/utils/api.py +28 -0
  124. django_camomilla_cms-6.0.0/tests/utils/media.py +10 -0
  125. django-camomilla-cms-5.8.5/PKG-INFO +0 -54
  126. django-camomilla-cms-5.8.5/README.md +0 -36
  127. django-camomilla-cms-5.8.5/camomilla/__init__.py +0 -5
  128. django-camomilla-cms-5.8.5/camomilla/admin.py +0 -98
  129. django-camomilla-cms-5.8.5/camomilla/apps.py +0 -8
  130. django-camomilla-cms-5.8.5/camomilla/migrations/0001_initial.py +0 -577
  131. django-camomilla-cms-5.8.5/camomilla/migrations/0002_auto_20200214_1127.py +0 -33
  132. django-camomilla-cms-5.8.5/camomilla/migrations/0003_auto_20210130_1610.py +0 -30
  133. django-camomilla-cms-5.8.5/camomilla/migrations/0004_auto_20210511_0937.py +0 -25
  134. django-camomilla-cms-5.8.5/camomilla/migrations/0005_media_image_props.py +0 -19
  135. django-camomilla-cms-5.8.5/camomilla/migrations/0006_auto_20220103_1845.py +0 -35
  136. django-camomilla-cms-5.8.5/camomilla/migrations/0007_auto_20220211_1622.py +0 -18
  137. django-camomilla-cms-5.8.5/camomilla/migrations/0008_auto_20220309_1616.py +0 -60
  138. django-camomilla-cms-5.8.5/camomilla/migrations/0009_article__hvad_query_category__hvad_query_and_more.py +0 -165
  139. django-camomilla-cms-5.8.5/camomilla/migrations/0010_auto_20220802_1406.py +0 -83
  140. django-camomilla-cms-5.8.5/camomilla/migrations/0011_auto_20220902_1000.py +0 -15
  141. django-camomilla-cms-5.8.5/camomilla/models/__init__.py +0 -6
  142. django-camomilla-cms-5.8.5/camomilla/models/article.py +0 -62
  143. django-camomilla-cms-5.8.5/camomilla/models/category.py +0 -25
  144. django-camomilla-cms-5.8.5/camomilla/models/media.py +0 -236
  145. django-camomilla-cms-5.8.5/camomilla/models/mixins/__init__.py +0 -77
  146. django-camomilla-cms-5.8.5/camomilla/models/page.py +0 -32
  147. django-camomilla-cms-5.8.5/camomilla/models/tag.py +0 -19
  148. django-camomilla-cms-5.8.5/camomilla/serializers/article.py +0 -20
  149. django-camomilla-cms-5.8.5/camomilla/serializers/base/__init__.py +0 -35
  150. django-camomilla-cms-5.8.5/camomilla/serializers/fields/__init__.py +0 -23
  151. django-camomilla-cms-5.8.5/camomilla/serializers/mixins/__init__.py +0 -187
  152. django-camomilla-cms-5.8.5/camomilla/serializers/page.py +0 -14
  153. django-camomilla-cms-5.8.5/camomilla/theme/__init__.py +0 -1
  154. django-camomilla-cms-5.8.5/camomilla/theme/static/admin/css/responsive.css +0 -1023
  155. django-camomilla-cms-5.8.5/camomilla/theme/static/admin/img/logo.png +0 -0
  156. django-camomilla-cms-5.8.5/camomilla/theme/templates/admin/base_site.html +0 -18
  157. django-camomilla-cms-5.8.5/camomilla/views/base/__init__.py +0 -8
  158. django-camomilla-cms-5.8.5/camomilla/views/categories.py +0 -13
  159. django-camomilla-cms-5.8.5/camomilla/views/mixins/__init__.py +0 -73
  160. django-camomilla-cms-5.8.5/camomilla/views/pages.py +0 -13
  161. django-camomilla-cms-5.8.5/camomilla/views/tags.py +0 -13
  162. django-camomilla-cms-5.8.5/django_camomilla_cms.egg-info/PKG-INFO +0 -54
  163. django-camomilla-cms-5.8.5/django_camomilla_cms.egg-info/SOURCES.txt +0 -81
  164. django-camomilla-cms-5.8.5/django_camomilla_cms.egg-info/requires.txt +0 -8
  165. django-camomilla-cms-5.8.5/setup.cfg +0 -41
  166. django-camomilla-cms-5.8.5/tests/test_api.py +0 -76
  167. django-camomilla-cms-5.8.5/tests/test_utils.py +0 -86
  168. django-camomilla-cms-5.8.5/tests/urls.py +0 -21
  169. {django-camomilla-cms-5.8.5 → django_camomilla_cms-6.0.0}/LICENSE +0 -0
  170. {django-camomilla-cms-5.8.5 → django_camomilla_cms-6.0.0}/MANIFEST.in +0 -0
  171. {django-camomilla-cms-5.8.5 → django_camomilla_cms-6.0.0}/camomilla/authentication.py +0 -0
  172. {django-camomilla-cms-5.8.5/camomilla/management → django_camomilla_cms-6.0.0/camomilla/contrib}/__init__.py +0 -0
  173. {django-camomilla-cms-5.8.5/camomilla/management/commands → django_camomilla_cms-6.0.0/camomilla/contrib/modeltranslation}/__init__.py +0 -0
  174. {django-camomilla-cms-5.8.5 → django_camomilla_cms-6.0.0}/camomilla/defaults.py +0 -0
  175. {django-camomilla-cms-5.8.5 → django_camomilla_cms-6.0.0}/camomilla/exceptions.py +0 -0
  176. {django-camomilla-cms-5.8.5/camomilla/migrations → django_camomilla_cms-6.0.0/camomilla/management}/__init__.py +0 -0
  177. {django-camomilla-cms-5.8.5/camomilla/templatetags → django_camomilla_cms-6.0.0/camomilla/management/commands}/__init__.py +0 -0
  178. {django-camomilla-cms-5.8.5/tests → django_camomilla_cms-6.0.0/camomilla/openapi}/__init__.py +0 -0
  179. {django-camomilla-cms-5.8.5 → django_camomilla_cms-6.0.0}/camomilla/permissions.py +0 -0
  180. {django-camomilla-cms-5.8.5/camomilla/templates/camomilla → django_camomilla_cms-6.0.0/camomilla/templates/defaults}/widgets/media_select_multiple.html +0 -0
  181. {django-camomilla-cms-5.8.5 → django_camomilla_cms-6.0.0}/camomilla/views/languages.py +0 -0
  182. {django-camomilla-cms-5.8.5 → django_camomilla_cms-6.0.0}/django_camomilla_cms.egg-info/dependency_links.txt +0 -0
  183. {django-camomilla-cms-5.8.5 → django_camomilla_cms-6.0.0}/django_camomilla_cms.egg-info/top_level.txt +0 -0
  184. {django-camomilla-cms-5.8.5 → django_camomilla_cms-6.0.0}/tests/test_models.py +0 -0
@@ -0,0 +1,123 @@
1
+ Metadata-Version: 2.4
2
+ Name: django-camomilla-cms
3
+ Version: 6.0.0
4
+ Summary: Django powered cms
5
+ Author-email: Lotrèk <dimmitutto@lotrek.it>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/camomillacms/camomilla-core
8
+ Keywords: cms,django,api cms
9
+ Classifier: Environment :: Web Environment
10
+ Classifier: Framework :: Django
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python
13
+ Classifier: Programming Language :: Python :: 3
14
+ Requires-Python: <=3.13,>=3.8
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: django-modeltranslation<=0.18.12,>=0.18.7
18
+ Requires-Dist: djsuperadmin<1.0.0,>=0.9
19
+ Requires-Dist: djangorestframework<=3.14.0,>=3.10.0
20
+ Requires-Dist: django-structured-json-field>=0.4.2
21
+ Requires-Dist: Pillow>=10.0.0
22
+ Requires-Dist: django-admin-interface<1.0.0,>=0.26.0
23
+ Requires-Dist: django-ckeditor<7.0.0,>=5.7.1
24
+ Requires-Dist: django-tinymce<5.0.0,>=4.1.0
25
+ Requires-Dist: python-magic<0.5,>=0.4
26
+ Requires-Dist: Django<6,>=3.2
27
+ Requires-Dist: django_jsonform>=2.23
28
+ Dynamic: license-file
29
+
30
+ [![PyPI](https://img.shields.io/pypi/v/django-camomilla-cms?style=flat-square)](https://pypi.org/project/django-camomilla-cms) ![Codecov](https://img.shields.io/codecov/c/github/camomillacms/camomilla-core?style=flat-square) [![License](https://img.shields.io/github/license/camomillacms/camomilla-core?style=flat-square)](./LICENSE)
31
+
32
+ <br>
33
+ <br>
34
+ <br>
35
+ <br>
36
+ <div align="center">
37
+ <picture>
38
+ <source media="(prefers-color-scheme: dark)" srcset="https://camomillacms.github.io/camomilla-core/images/camomilla-logo-dark.svg?v=1">
39
+ <source media="(prefers-color-scheme: light)" srcset="https://camomillacms.github.io/camomilla-core/images/camomilla-logo-light.svg?v=1">
40
+ <img alt="Fallback image description" src="https://camomillacms.github.io/camomilla-core/images/camomilla-logo-light.svg?v=1" style="width: 250px; height: auto;">
41
+ </picture>
42
+ </div>
43
+ <h3 align="center"">Our beloved Django CMS</h3>
44
+ <br>
45
+
46
+ ## ⭐️ Features
47
+
48
+ <!-- Highlight some of the features your module provide here -->
49
+
50
+ - 🧘‍♀️ &nbsp;Built on top of the django framework
51
+ - 🥨 &nbsp;Beaked page abstract model to let you manage everything you need as a page.
52
+ - 🏞️ &nbsp;Optimized media management with autoresize
53
+ - 👯 &nbsp;Enable relations inside django JSONFields
54
+ - ⚡️ &nbsp;AutoCreate api endpoints from models
55
+ - 🚧 &nbsp;Enable JsonSchema directly in models endpoints
56
+
57
+ Camomilla is a Django CMS that allows you to create and manage your website's content with ease. It provides a simple and intuitive interface for managing pages, media, and other content types. Camomilla is built on top of the Django framework, which means it inherits all the features and benefits of Django framework.
58
+ We try to continuously improve Camomilla by adding new features and fixing bugs. You can check the [CHANGELOG](./CHANGELOG.md) to see what has been added in the latest releases.
59
+
60
+ ## 📦 Quick Start
61
+
62
+ Here you can find some quick setup instructions to get started with Camomilla. For more detailed information, please refer to the [documentation](https://camomillacms.github.io/camomilla-core/).
63
+
64
+ > [!TIP]
65
+ >
66
+ > #### Env Virtualization 👾
67
+ >
68
+ > Use a virtualenv to isolate your project's dependencies from the system's python installation before starting. Check out [virtualenvwrapper](https://virtualenvwrapper.readthedocs.io/en/latest/) for more information.
69
+
70
+ Install django-camomilla-cms and django from pip
71
+
72
+ ```bash
73
+ $ pip install django
74
+ $ pip install django-camomilla-cms==6.0.0
75
+ ```
76
+
77
+ Create a new django project
78
+
79
+ ```bash
80
+ $ django-admin startproject <project_name>
81
+ $ cd <project_name>
82
+ ```
83
+
84
+ Create a dedicated folder for camomilla migrations
85
+
86
+ ```bash
87
+ $ mkdir -p camomilla_migrations
88
+ $ touch camomilla_migrations.__init__.py
89
+ ```
90
+
91
+ Create migrations and prepare the database
92
+
93
+ ```bash
94
+ $ python manage.py makemigrations camomilla
95
+ $ python manage.py migrate
96
+ ```
97
+
98
+ Add camomilla and camomilla dependencies to your project's INSTALLED_APPS
99
+
100
+ ```python
101
+ # <project_name>/settings.py
102
+
103
+ INSTALLED_APPS = [
104
+ ...
105
+ 'camomilla', # always needed
106
+ 'camomilla.theme', # needed to customize admin interface
107
+ 'djsuperadmin', # needed if you whant to use djsuperadmin for contents
108
+ 'modeltranslation', # needed if your website is multilanguage (can be added later)
109
+ 'rest_framework', # always needed
110
+ 'rest_framework.authtoken', # always needed
111
+ ...
112
+ ]
113
+ ```
114
+
115
+ Run the server
116
+
117
+ ```bash
118
+ $ python manage.py runserver
119
+ ```
120
+
121
+ ## 🧑‍💻 How to Contribute
122
+
123
+ We welcome contributions to Camomilla! If you want to contribute, please read our [contributing guide](./CONTRIBUTING.md) for more information on how to get started.
@@ -0,0 +1,94 @@
1
+ [![PyPI](https://img.shields.io/pypi/v/django-camomilla-cms?style=flat-square)](https://pypi.org/project/django-camomilla-cms) ![Codecov](https://img.shields.io/codecov/c/github/camomillacms/camomilla-core?style=flat-square) [![License](https://img.shields.io/github/license/camomillacms/camomilla-core?style=flat-square)](./LICENSE)
2
+
3
+ <br>
4
+ <br>
5
+ <br>
6
+ <br>
7
+ <div align="center">
8
+ <picture>
9
+ <source media="(prefers-color-scheme: dark)" srcset="https://camomillacms.github.io/camomilla-core/images/camomilla-logo-dark.svg?v=1">
10
+ <source media="(prefers-color-scheme: light)" srcset="https://camomillacms.github.io/camomilla-core/images/camomilla-logo-light.svg?v=1">
11
+ <img alt="Fallback image description" src="https://camomillacms.github.io/camomilla-core/images/camomilla-logo-light.svg?v=1" style="width: 250px; height: auto;">
12
+ </picture>
13
+ </div>
14
+ <h3 align="center"">Our beloved Django CMS</h3>
15
+ <br>
16
+
17
+ ## ⭐️ Features
18
+
19
+ <!-- Highlight some of the features your module provide here -->
20
+
21
+ - 🧘‍♀️ &nbsp;Built on top of the django framework
22
+ - 🥨 &nbsp;Beaked page abstract model to let you manage everything you need as a page.
23
+ - 🏞️ &nbsp;Optimized media management with autoresize
24
+ - 👯 &nbsp;Enable relations inside django JSONFields
25
+ - ⚡️ &nbsp;AutoCreate api endpoints from models
26
+ - 🚧 &nbsp;Enable JsonSchema directly in models endpoints
27
+
28
+ Camomilla is a Django CMS that allows you to create and manage your website's content with ease. It provides a simple and intuitive interface for managing pages, media, and other content types. Camomilla is built on top of the Django framework, which means it inherits all the features and benefits of Django framework.
29
+ We try to continuously improve Camomilla by adding new features and fixing bugs. You can check the [CHANGELOG](./CHANGELOG.md) to see what has been added in the latest releases.
30
+
31
+ ## 📦 Quick Start
32
+
33
+ Here you can find some quick setup instructions to get started with Camomilla. For more detailed information, please refer to the [documentation](https://camomillacms.github.io/camomilla-core/).
34
+
35
+ > [!TIP]
36
+ >
37
+ > #### Env Virtualization 👾
38
+ >
39
+ > Use a virtualenv to isolate your project's dependencies from the system's python installation before starting. Check out [virtualenvwrapper](https://virtualenvwrapper.readthedocs.io/en/latest/) for more information.
40
+
41
+ Install django-camomilla-cms and django from pip
42
+
43
+ ```bash
44
+ $ pip install django
45
+ $ pip install django-camomilla-cms==6.0.0
46
+ ```
47
+
48
+ Create a new django project
49
+
50
+ ```bash
51
+ $ django-admin startproject <project_name>
52
+ $ cd <project_name>
53
+ ```
54
+
55
+ Create a dedicated folder for camomilla migrations
56
+
57
+ ```bash
58
+ $ mkdir -p camomilla_migrations
59
+ $ touch camomilla_migrations.__init__.py
60
+ ```
61
+
62
+ Create migrations and prepare the database
63
+
64
+ ```bash
65
+ $ python manage.py makemigrations camomilla
66
+ $ python manage.py migrate
67
+ ```
68
+
69
+ Add camomilla and camomilla dependencies to your project's INSTALLED_APPS
70
+
71
+ ```python
72
+ # <project_name>/settings.py
73
+
74
+ INSTALLED_APPS = [
75
+ ...
76
+ 'camomilla', # always needed
77
+ 'camomilla.theme', # needed to customize admin interface
78
+ 'djsuperadmin', # needed if you whant to use djsuperadmin for contents
79
+ 'modeltranslation', # needed if your website is multilanguage (can be added later)
80
+ 'rest_framework', # always needed
81
+ 'rest_framework.authtoken', # always needed
82
+ ...
83
+ ]
84
+ ```
85
+
86
+ Run the server
87
+
88
+ ```bash
89
+ $ python manage.py runserver
90
+ ```
91
+
92
+ ## 🧑‍💻 How to Contribute
93
+
94
+ We welcome contributions to Camomilla! If you want to contribute, please read our [contributing guide](./CONTRIBUTING.md) for more information on how to get started.
@@ -0,0 +1,11 @@
1
+ __version__ = "6.0.0"
2
+
3
+
4
+ def get_core_apps():
5
+ return ["rest_framework", "rest_framework.authtoken"]
6
+
7
+
8
+ def autodiscover():
9
+ from camomilla.templates_context.autodiscover import autodiscover_context_files
10
+
11
+ autodiscover_context_files()
@@ -0,0 +1,16 @@
1
+ from __future__ import unicode_literals
2
+
3
+ from django.apps import AppConfig
4
+ from django.conf import settings
5
+
6
+
7
+ class CamomillaConfig(AppConfig):
8
+ default_auto_field = "django.db.models.AutoField"
9
+ name = "camomilla"
10
+
11
+ def ready(self):
12
+ migration_modules = getattr(settings, "MIGRATION_MODULES", {})
13
+ if "camomilla" not in migration_modules:
14
+ migration_modules["camomilla"] = "camomilla_migrations"
15
+ setattr(settings, "MIGRATION_MODULES", migration_modules)
16
+ self.module.autodiscover()
@@ -0,0 +1,6 @@
1
+ from camomilla.models import Menu
2
+
3
+
4
+ def MenusContextProcessor(request):
5
+ qs = Menu.objects.all()
6
+ return {"menus": Menu.defaultdict(**{m.key: m for m in qs})}
@@ -0,0 +1,126 @@
1
+ from django.conf import settings
2
+ from django.db import migrations, connection
3
+
4
+
5
+ class KeepTranslationsMixin:
6
+ """
7
+ This mixin make it possible to keep translations when migrating from django-hvad to modeltranslation and viceversa.
8
+ To use it, you have to add a dictionary to your migration class called "keep_translations".
9
+ The dictionary must have model paths as keys and a list of fields to keep as values.
10
+
11
+
12
+ Example:
13
+ ```python
14
+ class Migration(KeepTranslationsMixin, migrations.Migration):
15
+ keep_translations = {
16
+ "app.Model": ("field1", "field2", "field3")
17
+ }
18
+ ```
19
+ """
20
+
21
+ _saved_data_from_plain = {}
22
+ language_codes = dict(getattr(settings, "LANGUAGES", {})).keys()
23
+
24
+ def is_operation_legit(self, o):
25
+ return not (
26
+ isinstance(o, migrations.RemoveField)
27
+ and o.name == "master"
28
+ and o.model_name.endswith("translation")
29
+ )
30
+
31
+ def __init__(self, *args, **kwargs):
32
+ super().__init__(*args, **kwargs)
33
+ self.operations = [o for o in self.operations if self.is_operation_legit(o)]
34
+ self.operations.insert(
35
+ 0, migrations.RunPython(self._getDataFromHvad, self._restoreDataToHvad)
36
+ )
37
+ self.operations.append(
38
+ migrations.RunPython(
39
+ self._restoreDataToModelTranslation, self._getDataFromModelTranslation
40
+ )
41
+ )
42
+
43
+ def _getDataFromHvad(self, apps, schemaeditor):
44
+ for modelPath, fields in self.keep_translations.items():
45
+ Model = apps.get_model(*modelPath.split("."))
46
+ table = Model._meta.db_table + "_translation"
47
+ if "language_code" not in fields:
48
+ fields = ("language_code",) + fields
49
+ with connection.cursor() as cursor:
50
+ cursor.execute("SELECT master_id FROM {0};".format(table))
51
+ masters = list(set(cursor.fetchall()))
52
+ for master in masters:
53
+ cursor.execute(
54
+ "SELECT {0} FROM {1} WHERE master_id={2};".format(
55
+ ",".join(fields), table, master[0]
56
+ )
57
+ )
58
+ rows = cursor.fetchall()
59
+ self._saved_data_from_plain[modelPath] = (
60
+ self._saved_data_from_plain.get(modelPath, {})
61
+ )
62
+ self._saved_data_from_plain[modelPath][master[0]] = (
63
+ self._saved_data_from_plain[modelPath].get(master[0], [])
64
+ )
65
+ for row in rows:
66
+ self._saved_data_from_plain[modelPath][master[0]].append(
67
+ dict(zip(fields, row))
68
+ )
69
+
70
+ def _getDataFromModelTranslation(self, apps, schemaeditor):
71
+ for modelPath, fields in self.keep_translations.items():
72
+ Model = apps.get_model(*modelPath.split("."))
73
+ table = Model._meta.db_table
74
+ for lang in self.language_codes:
75
+ t_fields = ("id",) + tuple(
76
+ "{0}_{1}".format(f, lang) for f in fields if f != "id"
77
+ )
78
+ with connection.cursor() as cursor:
79
+ cursor.execute(
80
+ "SELECT {0} FROM {1}".format(
81
+ ",".join(t_fields),
82
+ table,
83
+ )
84
+ )
85
+ rows = cursor.fetchall()
86
+ self._saved_data_from_plain[modelPath] = (
87
+ self._saved_data_from_plain.get(modelPath, [])
88
+ )
89
+ for row in rows:
90
+ row_data = dict(zip(("master_id", *fields), row))
91
+ row_data.update({"language_code": lang})
92
+ self._saved_data_from_plain[modelPath].append(row_data)
93
+
94
+ def _restoreDataToModelTranslation(self, apps, schemaeditor):
95
+ for key, master_dict in self._saved_data_from_plain.items():
96
+ Model = apps.get_model(*key.split("."))
97
+ for pk, translations in master_dict.items():
98
+ try:
99
+ obj = Model.objects.get(pk=pk)
100
+ except Model.DoesNotExist:
101
+ continue
102
+ for translation in translations:
103
+ lang = translation.pop("language_code")
104
+ for attr, value in translation.items():
105
+ setattr(obj, "{0}_{1}".format(attr, lang), value)
106
+ obj.save()
107
+
108
+ def _restoreDataToHvad(self, apps, schemaeditor):
109
+ for key, rows in self._saved_data_from_plain.items():
110
+ Model = apps.get_model(*key.split("."))
111
+ table = Model._meta.db_table + "_translation"
112
+ for row in rows:
113
+ with connection.cursor() as cursor:
114
+ print(row)
115
+ cursor.execute(
116
+ "INSERT INTO {0} ({1}) VALUES ({2});".format(
117
+ table,
118
+ ",".join(row.keys()),
119
+ ",".join(
120
+ [
121
+ "'{}'".format(v).replace("'None'", "NULL")
122
+ for v in row.values()
123
+ ]
124
+ ),
125
+ )
126
+ )
@@ -0,0 +1,33 @@
1
+ from django.shortcuts import redirect, render
2
+ from django.urls import path
3
+
4
+ from camomilla import settings
5
+ from django.conf import settings as django_settings
6
+ from .models import Page, UrlRedirect
7
+
8
+
9
+ def fetch(request, *args, **kwargs):
10
+ can_preview = request.user.is_staff or settings.DEBUG
11
+ preview = can_preview and request.GET.get("preview", False)
12
+ append_slash = getattr(django_settings, "APPEND_SLASH", True)
13
+ redirect_obj = UrlRedirect.find_redirect(request)
14
+ if redirect_obj:
15
+ return redirect_obj.redirect()
16
+ if append_slash and not request.path.endswith("/"):
17
+ q_string = request.META.get("QUERY_STRING", "")
18
+ return redirect(request.path + "/" + ("?" + q_string if q_string else ""))
19
+ if "permalink" in kwargs:
20
+ page = Page.get_or_404(
21
+ request, bypass_public_check=preview, bypass_type_check=True
22
+ )
23
+ elif settings.AUTO_CREATE_HOMEPAGE is False:
24
+ page, _ = Page.get_or_404(permalink="/", bypass_type_check=True)
25
+ else:
26
+ page, _ = Page.get_or_create_homepage()
27
+ return render(request, page.get_template_path(request), page.get_context(request))
28
+
29
+
30
+ urlpatterns = [
31
+ path("", fetch, name="camomilla-homepage"),
32
+ path("<path:permalink>", fetch, name="camomilla-permalink"),
33
+ ]
@@ -0,0 +1,13 @@
1
+ from django.db import models
2
+
3
+ from .json import ArrayField, JSONField
4
+
5
+ ORDERING_ACCEPTED_FIELDS = (
6
+ models.BigIntegerField,
7
+ models.IntegerField,
8
+ models.PositiveIntegerField,
9
+ models.PositiveSmallIntegerField,
10
+ models.SmallIntegerField,
11
+ )
12
+
13
+ __all__ = ["JSONField", "ArrayField", "ORDERING_ACCEPTED_FIELDS"]
@@ -1,32 +1,18 @@
1
1
  import json
2
+
2
3
  import django
3
4
  from django.conf import settings
5
+
4
6
  if django.VERSION >= (4, 0):
5
7
  from django.db.models import JSONField as DjangoJSONField
6
8
  else:
7
9
  from django.contrib.postgres.fields import JSONField as DjangoJSONField
10
+
8
11
  from django.contrib.postgres.fields import ArrayField as DjangoArrayField
9
12
  from django.db import models
10
13
 
11
14
 
12
- ORDERING_ACCEPTED_FIELDS = (
13
- models.BigIntegerField,
14
- models.IntegerField,
15
- models.PositiveIntegerField,
16
- models.PositiveSmallIntegerField,
17
- models.SmallIntegerField,
18
- )
19
-
20
-
21
- class JSONField(DjangoJSONField):
22
- pass
23
-
24
-
25
- class ArrayField(DjangoArrayField):
26
- pass
27
-
28
-
29
- if "sqlite" in settings.DATABASES["default"]["ENGINE"]:
15
+ if "sqlite" in settings.DATABASES["default"]["ENGINE"]: # noqa: C901
30
16
 
31
17
  class JSONField(models.Field):
32
18
  def db_type(self, connection):
@@ -70,3 +56,14 @@ if "sqlite" in settings.DATABASES["default"]["ENGINE"]:
70
56
  }
71
57
  )
72
58
  return name, path, args, kwargs
59
+
60
+ else:
61
+
62
+ class JSONField(DjangoJSONField):
63
+ pass
64
+
65
+ class ArrayField(DjangoArrayField):
66
+ pass
67
+
68
+
69
+ __all__ = [JSONField, ArrayField]
@@ -3,7 +3,6 @@ from camomilla.models import Media
3
3
 
4
4
 
5
5
  class Command(BaseCommand):
6
-
7
6
  help = "Regenerates all the thumbnail"
8
7
 
9
8
  def handle(self, *args, **options):
@@ -0,0 +1,3 @@
1
+ from .pages import PageQuerySet
2
+
3
+ __all__ = ["PageQuerySet"]
@@ -0,0 +1,116 @@
1
+ from django.db.models.query import QuerySet
2
+ from django.core.exceptions import ObjectDoesNotExist
3
+ from django.apps import apps
4
+ from django.db import models
5
+ from django.utils import timezone
6
+ from django.db.utils import ProgrammingError, OperationalError
7
+ from typing import Sequence, Tuple
8
+
9
+ URL_NODE_RELATED_NAME = "%(app_label)s_%(class)s"
10
+
11
+
12
+ class PageQuerySet(QuerySet):
13
+
14
+ __UrlNodeModel = None
15
+
16
+ @property
17
+ def UrlNodeModel(self):
18
+ if not self.__UrlNodeModel:
19
+ self.__UrlNodeModel = apps.get_model("camomilla", "UrlNode")
20
+ return self.__UrlNodeModel
21
+
22
+ def get_permalink_kwargs(self, kwargs):
23
+ return list(
24
+ set(kwargs.keys()).intersection(
25
+ set(self.UrlNodeModel.LANG_PERMALINK_FIELDS + ["permalink"])
26
+ )
27
+ )
28
+
29
+ def get(self, *args, **kwargs):
30
+ permalink_args = self.get_permalink_kwargs(kwargs)
31
+ if len(permalink_args):
32
+ try:
33
+ node = self.UrlNodeModel.objects.get(
34
+ **{arg: kwargs.pop(arg) for arg in permalink_args}
35
+ )
36
+ kwargs["url_node"] = node
37
+ except ObjectDoesNotExist:
38
+ raise self.model.DoesNotExist(
39
+ "%s matching query does not exist." % self.model._meta.object_name
40
+ )
41
+ return super(PageQuerySet, self).get(*args, **kwargs)
42
+
43
+
44
+ class UrlNodeManager(models.Manager):
45
+ @property
46
+ def related_names(self):
47
+ self._related_names = getattr(
48
+ self,
49
+ "_related_names",
50
+ super().get_queryset().values_list("related_name", flat=True).distinct(),
51
+ )
52
+ return self._related_names
53
+
54
+ def _annotate_fields(
55
+ self,
56
+ qs: models.QuerySet,
57
+ field_names: Sequence[Tuple[str, models.Field, models.Value]],
58
+ ):
59
+ for field_name, output_field, default in field_names:
60
+ whens = [
61
+ models.When(
62
+ related_name=related_name,
63
+ then=models.F("__".join([related_name, field_name])),
64
+ )
65
+ for related_name in self.related_names
66
+ ]
67
+ qs = qs.annotate(
68
+ **{
69
+ field_name: models.Case(
70
+ *whens, output_field=output_field, default=default
71
+ )
72
+ }
73
+ )
74
+ return self._annotate_is_public(qs)
75
+
76
+ def _annotate_is_public(self, qs: models.QuerySet):
77
+ return qs.annotate(
78
+ is_public=models.Case(
79
+ models.When(status="PUB", then=True),
80
+ models.When(
81
+ status="PLA", publication_date__lte=timezone.now(), then=True
82
+ ),
83
+ default=False,
84
+ output_field=models.BooleanField(default=False),
85
+ )
86
+ )
87
+
88
+ def get_queryset(self):
89
+ try:
90
+ return self._annotate_fields(
91
+ super().get_queryset(),
92
+ [
93
+ (
94
+ "indexable",
95
+ models.BooleanField(),
96
+ models.Value(None, models.BooleanField()),
97
+ ),
98
+ (
99
+ "status",
100
+ models.CharField(),
101
+ models.Value("DRF", models.CharField()),
102
+ ),
103
+ (
104
+ "publication_date",
105
+ models.DateTimeField(),
106
+ models.Value(timezone.now(), models.DateTimeField()),
107
+ ),
108
+ (
109
+ "date_updated_at",
110
+ models.DateTimeField(),
111
+ models.Value(timezone.now(), models.DateTimeField()),
112
+ ),
113
+ ],
114
+ )
115
+ except (ProgrammingError, OperationalError):
116
+ return super().get_queryset()