wagtail 7.2.1__py3-none-any.whl → 7.3rc1__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 (312) hide show
  1. wagtail/__init__.py +1 -1
  2. wagtail/actions/copy_for_translation.py +4 -2
  3. wagtail/admin/action_menu.py +4 -1
  4. wagtail/admin/api/actions/convert_alias.py +2 -2
  5. wagtail/admin/api/actions/copy.py +2 -2
  6. wagtail/admin/api/actions/copy_for_translation.py +2 -2
  7. wagtail/admin/api/actions/create_alias.py +2 -2
  8. wagtail/admin/api/actions/delete.py +1 -1
  9. wagtail/admin/api/actions/move.py +1 -1
  10. wagtail/admin/api/actions/publish.py +2 -2
  11. wagtail/admin/api/actions/revert_to_page_revision.py +2 -2
  12. wagtail/admin/api/actions/unpublish.py +1 -1
  13. wagtail/admin/api/filters.py +2 -2
  14. wagtail/admin/compare.py +22 -0
  15. wagtail/admin/forms/account.py +52 -1
  16. wagtail/admin/forms/comments.py +53 -0
  17. wagtail/admin/forms/models.py +36 -0
  18. wagtail/admin/forms/pages.py +7 -0
  19. wagtail/admin/forms/workflows.py +5 -2
  20. wagtail/admin/icons.py +4 -3
  21. wagtail/admin/locale/ar/LC_MESSAGES/django.mo +0 -0
  22. wagtail/admin/locale/ar/LC_MESSAGES/django.po +35 -0
  23. wagtail/admin/locale/en/LC_MESSAGES/django.po +262 -234
  24. wagtail/admin/locale/en/LC_MESSAGES/djangojs.po +72 -43
  25. wagtail/admin/locale/it/LC_MESSAGES/django.mo +0 -0
  26. wagtail/admin/locale/it/LC_MESSAGES/django.po +566 -6
  27. wagtail/admin/locale/it/LC_MESSAGES/djangojs.mo +0 -0
  28. wagtail/admin/locale/it/LC_MESSAGES/djangojs.po +40 -2
  29. wagtail/admin/locale/nl/LC_MESSAGES/django.mo +0 -0
  30. wagtail/admin/locale/nl/LC_MESSAGES/django.po +2 -2
  31. wagtail/admin/locale/sv/LC_MESSAGES/django.mo +0 -0
  32. wagtail/admin/locale/sv/LC_MESSAGES/django.po +330 -15
  33. wagtail/admin/locale/sv/LC_MESSAGES/djangojs.mo +0 -0
  34. wagtail/admin/locale/sv/LC_MESSAGES/djangojs.po +3 -2
  35. wagtail/admin/panels/comment_panel.py +1 -51
  36. wagtail/admin/panels/title_field_panel.py +3 -1
  37. wagtail/admin/static/wagtailadmin/css/core.css +1 -1
  38. wagtail/admin/static/wagtailadmin/js/bulk-actions.js +1 -1
  39. wagtail/admin/static/wagtailadmin/js/comments.js +1 -1
  40. wagtail/admin/static/wagtailadmin/js/core.js +1 -1
  41. wagtail/admin/static/wagtailadmin/js/core.js.LICENSE.txt +1 -1
  42. wagtail/admin/static/wagtailadmin/js/draftail.js +1 -1
  43. wagtail/admin/static/wagtailadmin/js/privacy-switch.js +1 -1
  44. wagtail/admin/static/wagtailadmin/js/sidebar.js +1 -1
  45. wagtail/admin/static/wagtailadmin/js/telepath/blocks.js +1 -1
  46. wagtail/admin/static/wagtailadmin/js/userbar.js +1 -1
  47. wagtail/admin/static/wagtailadmin/js/userbar.js.LICENSE.txt +1 -1
  48. wagtail/admin/static/wagtailadmin/js/vendor.js +1 -1
  49. wagtail/admin/static/wagtailadmin/js/wagtailadmin.js +1 -1
  50. wagtail/admin/templates/wagtailadmin/base.html +1 -1
  51. wagtail/admin/templates/wagtailadmin/generic/edit_partials.html +100 -0
  52. wagtail/admin/templates/wagtailadmin/generic/form.html +26 -5
  53. wagtail/admin/templates/wagtailadmin/generic/includes/_loaded_revision_inputs.html +3 -0
  54. wagtail/admin/templates/wagtailadmin/generic/listing_results.html +1 -17
  55. wagtail/admin/templates/wagtailadmin/pages/create.html +14 -4
  56. wagtail/admin/templates/wagtailadmin/pages/edit.html +16 -3
  57. wagtail/admin/templates/wagtailadmin/pages/edit_partials.html +31 -0
  58. wagtail/admin/templates/wagtailadmin/pages/index_results.html +1 -9
  59. wagtail/admin/templates/wagtailadmin/shared/autosave/indicator.html +36 -0
  60. wagtail/admin/templates/wagtailadmin/shared/breadcrumbs.html +1 -1
  61. wagtail/admin/templates/wagtailadmin/shared/editing_sessions/list.html +2 -2
  62. wagtail/admin/templates/wagtailadmin/shared/editing_sessions/module.html +19 -3
  63. wagtail/admin/templates/wagtailadmin/shared/headers/_actions.html +5 -0
  64. wagtail/admin/templates/wagtailadmin/shared/headers/_history_icon_link.html +2 -2
  65. wagtail/admin/templates/wagtailadmin/shared/headers/slim_header.html +8 -9
  66. wagtail/admin/templates/wagtailadmin/shared/listing/filter_partials.html +19 -0
  67. wagtail/admin/templates/wagtailadmin/shared/side_panel_toggle.html +3 -3
  68. wagtail/admin/templates/wagtailadmin/shared/side_panels/includes/side_panel.html +3 -0
  69. wagtail/admin/templates/wagtailadmin/shared/side_panels/includes/status/privacy.html +18 -6
  70. wagtail/admin/templates/wagtailadmin/shared/side_panels/includes/status/usage.html +11 -4
  71. wagtail/admin/templates/wagtailadmin/shared/side_panels/includes/status/workflow.html +22 -1
  72. wagtail/admin/templates/wagtailadmin/shared/side_panels/preview.html +1 -0
  73. wagtail/admin/templates/wagtailadmin/shared/side_panels.html +1 -2
  74. wagtail/admin/templates/wagtailadmin/shared/unsaved_changes_warning.html +20 -20
  75. wagtail/admin/templates/wagtailadmin/userbar/base.html +2 -0
  76. wagtail/admin/templates/wagtailadmin/userbar/item_accessibility.html +1 -1
  77. wagtail/admin/templatetags/wagtailadmin_tags.py +6 -14
  78. wagtail/admin/tests/pages/test_create_page.py +298 -1
  79. wagtail/admin/tests/pages/test_custom_listing.py +48 -2
  80. wagtail/admin/tests/pages/test_edit_page.py +721 -7
  81. wagtail/admin/tests/pages/test_explorer_view.py +9 -5
  82. wagtail/admin/tests/pages/test_page_search.py +15 -0
  83. wagtail/admin/tests/pages/test_revisions.py +4 -0
  84. wagtail/admin/tests/test_account_management.py +88 -2
  85. wagtail/admin/tests/test_collections_views.py +15 -15
  86. wagtail/admin/tests/test_compare.py +34 -0
  87. wagtail/admin/tests/test_editing_sessions.py +230 -8
  88. wagtail/admin/tests/test_forms.py +192 -1
  89. wagtail/admin/tests/test_icon_sprite.py +22 -2
  90. wagtail/admin/tests/test_page_chooser.py +13 -13
  91. wagtail/admin/tests/test_reports_views.py +4 -1
  92. wagtail/admin/tests/test_userbar.py +69 -5
  93. wagtail/admin/tests/test_views_generic.py +5 -5
  94. wagtail/admin/tests/test_workflows.py +14 -12
  95. wagtail/admin/tests/viewsets/test_model_viewset.py +13 -0
  96. wagtail/admin/ui/autosave.py +5 -0
  97. wagtail/admin/ui/editing_sessions.py +3 -0
  98. wagtail/admin/ui/side_panels.py +19 -20
  99. wagtail/admin/userbar.py +48 -27
  100. wagtail/admin/views/bulk_action/dispatcher.py +2 -2
  101. wagtail/admin/views/chooser.py +6 -6
  102. wagtail/admin/views/editing_sessions.py +20 -7
  103. wagtail/admin/views/generic/__init__.py +1 -0
  104. wagtail/admin/views/generic/chooser.py +5 -5
  105. wagtail/admin/views/generic/mixins.py +143 -26
  106. wagtail/admin/views/generic/models.py +157 -27
  107. wagtail/admin/views/generic/ordering.py +1 -1
  108. wagtail/admin/views/generic/preview.py +4 -4
  109. wagtail/admin/views/home.py +3 -1
  110. wagtail/admin/views/pages/create.py +77 -29
  111. wagtail/admin/views/pages/edit.py +160 -34
  112. wagtail/admin/views/pages/preview.py +4 -4
  113. wagtail/admin/views/pages/revisions.py +3 -0
  114. wagtail/admin/views/pages/search.py +4 -4
  115. wagtail/admin/views/pages/usage.py +2 -2
  116. wagtail/admin/views/tags.py +2 -2
  117. wagtail/admin/views/workflows.py +73 -54
  118. wagtail/admin/viewsets/model.py +34 -8
  119. wagtail/admin/widgets/button.py +11 -4
  120. wagtail/admin/widgets/chooser.py +6 -4
  121. wagtail/admin/widgets/slug.py +1 -1
  122. wagtail/api/v2/filters.py +27 -21
  123. wagtail/api/v2/pagination.py +4 -4
  124. wagtail/api/v2/views.py +7 -7
  125. wagtail/blocks/list_block.py +0 -8
  126. wagtail/blocks/migrations/migrate_operation.py +8 -1
  127. wagtail/blocks/stream_block.py +2 -10
  128. wagtail/blocks/struct_block.py +192 -12
  129. wagtail/compat.py +2 -2
  130. wagtail/contrib/forms/locale/en/LC_MESSAGES/django.po +1 -1
  131. wagtail/contrib/forms/locale/it/LC_MESSAGES/django.mo +0 -0
  132. wagtail/contrib/forms/locale/it/LC_MESSAGES/django.po +40 -2
  133. wagtail/contrib/frontend_cache/backends/azure.py +6 -6
  134. wagtail/contrib/frontend_cache/backends/cloudfront.py +2 -2
  135. wagtail/contrib/frontend_cache/utils.py +1 -1
  136. wagtail/contrib/redirects/forms.py +1 -1
  137. wagtail/contrib/redirects/locale/en/LC_MESSAGES/django.po +14 -14
  138. wagtail/contrib/redirects/locale/it/LC_MESSAGES/django.mo +0 -0
  139. wagtail/contrib/redirects/locale/it/LC_MESSAGES/django.po +15 -2
  140. wagtail/contrib/redirects/locale/sv/LC_MESSAGES/django.mo +0 -0
  141. wagtail/contrib/redirects/locale/sv/LC_MESSAGES/django.po +16 -3
  142. wagtail/contrib/redirects/middleware.py +11 -7
  143. wagtail/contrib/redirects/models.py +17 -1
  144. wagtail/contrib/redirects/tests/test_import_admin_views.py +3 -3
  145. wagtail/contrib/redirects/tests/test_redirects.py +56 -8
  146. wagtail/contrib/search_promotions/locale/en/LC_MESSAGES/django.po +6 -6
  147. wagtail/contrib/search_promotions/locale/it/LC_MESSAGES/django.mo +0 -0
  148. wagtail/contrib/search_promotions/locale/it/LC_MESSAGES/django.po +43 -2
  149. wagtail/contrib/search_promotions/tests.py +1 -1
  150. wagtail/contrib/search_promotions/views/settings.py +24 -25
  151. wagtail/contrib/settings/jinja2tags.py +2 -2
  152. wagtail/contrib/settings/locale/en/LC_MESSAGES/django.po +2 -2
  153. wagtail/contrib/settings/locale/it/LC_MESSAGES/django.mo +0 -0
  154. wagtail/contrib/settings/locale/it/LC_MESSAGES/django.po +6 -2
  155. wagtail/contrib/settings/locale/sv/LC_MESSAGES/django.mo +0 -0
  156. wagtail/contrib/settings/locale/sv/LC_MESSAGES/django.po +3 -2
  157. wagtail/contrib/settings/tests/generic/test_admin.py +118 -2
  158. wagtail/contrib/settings/tests/site_specific/test_admin.py +78 -15
  159. wagtail/contrib/settings/tests/site_specific/test_model.py +2 -2
  160. wagtail/contrib/settings/views.py +7 -0
  161. wagtail/contrib/simple_translation/locale/en/LC_MESSAGES/django.po +1 -1
  162. wagtail/contrib/simple_translation/locale/it/LC_MESSAGES/django.mo +0 -0
  163. wagtail/contrib/simple_translation/locale/it/LC_MESSAGES/django.po +5 -2
  164. wagtail/contrib/styleguide/locale/en/LC_MESSAGES/django.po +7 -7
  165. wagtail/contrib/styleguide/tests.py +2 -0
  166. wagtail/contrib/styleguide/views.py +4 -5
  167. wagtail/contrib/table_block/locale/ar/LC_MESSAGES/django.mo +0 -0
  168. wagtail/contrib/table_block/locale/ar/LC_MESSAGES/django.po +5 -1
  169. wagtail/contrib/table_block/locale/en/LC_MESSAGES/django.po +1 -1
  170. wagtail/contrib/table_block/locale/it/LC_MESSAGES/django.mo +0 -0
  171. wagtail/contrib/table_block/locale/it/LC_MESSAGES/django.po +5 -2
  172. wagtail/contrib/typed_table_block/blocks.py +37 -0
  173. wagtail/contrib/typed_table_block/locale/en/LC_MESSAGES/django.po +10 -10
  174. wagtail/contrib/typed_table_block/locale/sv/LC_MESSAGES/django.mo +0 -0
  175. wagtail/contrib/typed_table_block/locale/sv/LC_MESSAGES/django.po +4 -3
  176. wagtail/contrib/typed_table_block/tests.py +108 -0
  177. wagtail/coreutils.py +4 -4
  178. wagtail/documents/__init__.py +4 -4
  179. wagtail/documents/locale/en/LC_MESSAGES/django.po +15 -15
  180. wagtail/documents/locale/it/LC_MESSAGES/django.mo +0 -0
  181. wagtail/documents/locale/it/LC_MESSAGES/django.po +32 -2
  182. wagtail/documents/locale/sv/LC_MESSAGES/django.mo +0 -0
  183. wagtail/documents/locale/sv/LC_MESSAGES/django.po +32 -4
  184. wagtail/documents/models.py +1 -1
  185. wagtail/documents/tests/test_admin_views.py +19 -4
  186. wagtail/documents/tests/test_search.py +0 -2
  187. wagtail/documents/tests/test_views.py +6 -4
  188. wagtail/documents/views/bulk_actions/add_tags.py +1 -1
  189. wagtail/embeds/finders/facebook.py +3 -3
  190. wagtail/embeds/finders/instagram.py +3 -3
  191. wagtail/embeds/finders/oembed.py +7 -2
  192. wagtail/embeds/locale/en/LC_MESSAGES/django.po +1 -1
  193. wagtail/embeds/oembed_providers.py +8 -0
  194. wagtail/embeds/tests/test_embeds.py +35 -0
  195. wagtail/images/__init__.py +4 -4
  196. wagtail/images/admin_urls.py +3 -3
  197. wagtail/images/blocks.py +1 -1
  198. wagtail/images/formats.py +2 -2
  199. wagtail/images/image_operations.py +2 -2
  200. wagtail/images/locale/en/LC_MESSAGES/django.po +44 -40
  201. wagtail/images/locale/it/LC_MESSAGES/django.mo +0 -0
  202. wagtail/images/locale/it/LC_MESSAGES/django.po +50 -4
  203. wagtail/images/locale/nl/LC_MESSAGES/django.mo +0 -0
  204. wagtail/images/locale/nl/LC_MESSAGES/django.po +3 -3
  205. wagtail/images/locale/sv/LC_MESSAGES/django.mo +0 -0
  206. wagtail/images/locale/sv/LC_MESSAGES/django.po +49 -6
  207. wagtail/images/models.py +11 -10
  208. wagtail/images/templates/wagtailimages/images/index_results.html +1 -1
  209. wagtail/images/templates/wagtailimages/images/url_generator.html +17 -38
  210. wagtail/images/templates/wagtailimages/images/url_generator_output.html +28 -0
  211. wagtail/images/templatetags/wagtailimages_tags.py +4 -4
  212. wagtail/images/tests/test_admin_views.py +432 -59
  213. wagtail/images/tests/test_image_operations.py +2 -2
  214. wagtail/images/tests/test_models.py +0 -2
  215. wagtail/images/views/bulk_actions/add_tags.py +1 -1
  216. wagtail/images/views/images.py +72 -39
  217. wagtail/locale/en/LC_MESSAGES/django.po +103 -105
  218. wagtail/locale/it/LC_MESSAGES/django.mo +0 -0
  219. wagtail/locale/it/LC_MESSAGES/django.po +105 -2
  220. wagtail/locale/sv/LC_MESSAGES/django.mo +0 -0
  221. wagtail/locale/sv/LC_MESSAGES/django.po +95 -12
  222. wagtail/locales/locale/en/LC_MESSAGES/django.po +1 -1
  223. wagtail/locales/locale/it/LC_MESSAGES/django.mo +0 -0
  224. wagtail/locales/locale/it/LC_MESSAGES/django.po +13 -2
  225. wagtail/locales/locale/sv/LC_MESSAGES/django.mo +0 -0
  226. wagtail/locales/locale/sv/LC_MESSAGES/django.po +4 -3
  227. wagtail/locales/tests.py +5 -5
  228. wagtail/models/i18n.py +4 -6
  229. wagtail/models/media.py +18 -0
  230. wagtail/models/pages.py +65 -11
  231. wagtail/models/reference_index.py +15 -0
  232. wagtail/models/revisions.py +40 -12
  233. wagtail/models/workflows.py +0 -3
  234. wagtail/permission_policies/base.py +2 -2
  235. wagtail/permission_policies/collections.py +2 -2
  236. wagtail/project_template/requirements.txt +2 -2
  237. wagtail/search/locale/en/LC_MESSAGES/django.po +1 -1
  238. wagtail/signal_handlers.py +1 -0
  239. wagtail/sites/locale/en/LC_MESSAGES/django.po +1 -1
  240. wagtail/sites/locale/sv/LC_MESSAGES/django.mo +0 -0
  241. wagtail/sites/locale/sv/LC_MESSAGES/django.po +3 -2
  242. wagtail/sites/tests.py +7 -7
  243. wagtail/snippets/action_menu.py +2 -1
  244. wagtail/snippets/locale/en/LC_MESSAGES/django.po +23 -13
  245. wagtail/snippets/locale/sv/LC_MESSAGES/django.mo +0 -0
  246. wagtail/snippets/locale/sv/LC_MESSAGES/django.po +12 -1
  247. wagtail/snippets/tests/test_chooser_block.py +197 -0
  248. wagtail/snippets/tests/test_chooser_panel.py +149 -0
  249. wagtail/snippets/tests/test_chooser_views.py +375 -0
  250. wagtail/snippets/tests/test_chooser_widget.py +22 -0
  251. wagtail/snippets/tests/test_compare_revisions_view.py +167 -0
  252. wagtail/snippets/tests/test_copy_view.py +38 -0
  253. wagtail/snippets/tests/test_create_view.py +1159 -0
  254. wagtail/snippets/tests/test_delete_view.py +225 -0
  255. wagtail/snippets/tests/test_edit_view.py +2882 -0
  256. wagtail/snippets/tests/test_history_view.py +182 -0
  257. wagtail/snippets/tests/test_index_view.py +105 -0
  258. wagtail/snippets/tests/test_list_view.py +786 -0
  259. wagtail/snippets/tests/test_locking.py +28 -0
  260. wagtail/snippets/tests/test_permissions.py +167 -0
  261. wagtail/snippets/tests/test_preview.py +3 -3
  262. wagtail/snippets/tests/test_revert_view.py +343 -0
  263. wagtail/snippets/tests/test_snippet_models.py +112 -0
  264. wagtail/snippets/tests/test_unpublish_view.py +228 -0
  265. wagtail/snippets/tests/test_unschedule_view.py +151 -0
  266. wagtail/snippets/tests/test_viewset.py +38 -5
  267. wagtail/snippets/views/snippets.py +78 -30
  268. wagtail/snippets/widgets.py +2 -2
  269. wagtail/templatetags/wagtailcore_tags.py +2 -2
  270. wagtail/test/dummy_external_storage.py +1 -1
  271. wagtail/test/testapp/fixtures/test.json +22 -0
  272. wagtail/test/testapp/fixtures/test_empty.json +2 -0
  273. wagtail/test/testapp/fixtures/test_explorable_pages.json +10 -0
  274. wagtail/test/testapp/fixtures/test_specific.json +9 -0
  275. wagtail/test/testapp/migrations/0059_nopromotepage.py +25 -0
  276. wagtail/test/testapp/models.py +7 -0
  277. wagtail/test/testapp/views.py +5 -0
  278. wagtail/test/utils/page_tests.py +7 -7
  279. wagtail/test/utils/wagtail_factories/builder.py +2 -2
  280. wagtail/tests/streamfield_migrations/test_migrations.py +35 -0
  281. wagtail/tests/test_blocks.py +321 -61
  282. wagtail/tests/test_collection_model.py +12 -0
  283. wagtail/tests/test_page_model.py +190 -1
  284. wagtail/tests/test_page_privacy.py +5 -0
  285. wagtail/tests/test_reference_index.py +42 -0
  286. wagtail/tests/test_revision_model.py +118 -1
  287. wagtail/tests/test_utils.py +2 -2
  288. wagtail/users/locale/en/LC_MESSAGES/django.po +14 -14
  289. wagtail/users/locale/id_ID/LC_MESSAGES/django.mo +0 -0
  290. wagtail/users/locale/id_ID/LC_MESSAGES/django.po +6 -2
  291. wagtail/users/locale/it/LC_MESSAGES/django.mo +0 -0
  292. wagtail/users/locale/it/LC_MESSAGES/django.po +14 -2
  293. wagtail/users/locale/sv/LC_MESSAGES/django.mo +0 -0
  294. wagtail/users/locale/sv/LC_MESSAGES/django.po +48 -17
  295. wagtail/users/tests/test_admin_views.py +39 -25
  296. wagtail/users/utils.py +4 -1
  297. wagtail/users/views/groups.py +19 -5
  298. wagtail/users/wagtail_hooks.py +1 -1
  299. wagtail/utils/loading.py +2 -2
  300. wagtail-7.3rc1.data/data/AGENTS.md +21 -0
  301. wagtail-7.3rc1.data/data/CHANGELOG.txt +4941 -0
  302. wagtail-7.3rc1.data/data/CODE_OF_CONDUCT.md +71 -0
  303. wagtail-7.3rc1.data/data/CONTRIBUTORS.md +999 -0
  304. wagtail-7.3rc1.data/data/SPONSORS.md +55 -0
  305. {wagtail-7.2.1.dist-info → wagtail-7.3rc1.dist-info}/METADATA +6 -6
  306. {wagtail-7.2.1.dist-info → wagtail-7.3rc1.dist-info}/RECORD +310 -280
  307. {wagtail-7.2.1.dist-info → wagtail-7.3rc1.dist-info}/WHEEL +1 -1
  308. wagtail/images/static/wagtailimages/js/image-url-generator.js +0 -1
  309. wagtail/snippets/tests/test_snippets.py +0 -6377
  310. {wagtail-7.2.1.dist-info → wagtail-7.3rc1.dist-info}/entry_points.txt +0 -0
  311. {wagtail-7.2.1.dist-info → wagtail-7.3rc1.dist-info}/licenses/LICENSE +0 -0
  312. {wagtail-7.2.1.dist-info → wagtail-7.3rc1.dist-info}/top_level.txt +0 -0
wagtail/__init__.py CHANGED
@@ -6,7 +6,7 @@ from wagtail.utils.version import get_semver_version, get_version
6
6
 
7
7
  # major.minor.patch.release.number
8
8
  # release must be one of alpha, beta, rc, or final
9
- VERSION = (7, 2, 1, "final", 1)
9
+ VERSION = (7, 3, 0, "rc", 1)
10
10
 
11
11
  __version__ = get_version(VERSION)
12
12
 
@@ -107,9 +107,11 @@ class CopyPageForTranslationAction:
107
107
  if not parent.is_root():
108
108
  try:
109
109
  translated_parent = parent.get_translation(locale)
110
- except parent.__class__.DoesNotExist:
110
+ except parent.__class__.DoesNotExist as e:
111
111
  if not copy_parents:
112
- raise ParentNotTranslatedError("Parent page is not translated.")
112
+ raise ParentNotTranslatedError(
113
+ "Parent page is not translated."
114
+ ) from e
113
115
 
114
116
  translated_parent = parent.copy_for_translation(
115
117
  locale, copy_parents=True, alias=True
@@ -4,6 +4,7 @@ from django.conf import settings
4
4
  from django.forms import Media
5
5
  from django.template.loader import render_to_string
6
6
  from django.urls import reverse
7
+ from django.utils import timezone
7
8
  from django.utils.functional import cached_property
8
9
  from django.utils.translation import gettext_lazy as _
9
10
 
@@ -89,7 +90,9 @@ class PublishMenuItem(ActionMenuItem):
89
90
  def get_context_data(self, parent_context):
90
91
  context = super().get_context_data(parent_context)
91
92
  page = context.get("page")
92
- context["is_scheduled"] = page and page.go_live_at
93
+ context["is_scheduled"] = (
94
+ page and page.go_live_at and page.go_live_at > timezone.now()
95
+ )
93
96
  context["is_revision"] = context["view"] == "revisions_revert"
94
97
  return context
95
98
 
@@ -22,9 +22,9 @@ class ConvertAliasPageAPIAction(APIAction):
22
22
  try:
23
23
  new_page = action.execute()
24
24
  except DjangoValidationError as e:
25
- raise ValidationError(e.message_dict)
25
+ raise ValidationError(e.message_dict) from e
26
26
  except ConvertAliasPageError as e:
27
- raise BadRequestError(e.args[0])
27
+ raise BadRequestError(e.args[0]) from e
28
28
 
29
29
  serializer = self.view.get_serializer(new_page)
30
30
  return Response(serializer.data, status=status.HTTP_200_OK)
@@ -59,9 +59,9 @@ class CopyPageAPIAction(APIAction):
59
59
  try:
60
60
  new_page = action.execute()
61
61
  except DjangoValidationError as e:
62
- raise ValidationError(e.message_dict)
62
+ raise ValidationError(e.message_dict) from e
63
63
  except CopyPageIntegrityError as e:
64
- raise BadRequestError(e.args[0])
64
+ raise BadRequestError(e.args[0]) from e
65
65
 
66
66
  serializer = self.view.get_serializer(new_page)
67
67
  return Response(serializer.data, status=status.HTTP_201_CREATED)
@@ -43,9 +43,9 @@ class CopyForTranslationAPIAction(APIAction):
43
43
  try:
44
44
  translated_page = action.execute()
45
45
  except DjangoValidationError as e:
46
- raise ValidationError(e.message_dict)
46
+ raise ValidationError(e.message_dict) from e
47
47
  except ParentNotTranslatedError as e:
48
- raise BadRequestError(e.args[0])
48
+ raise BadRequestError(e.args[0]) from e
49
49
 
50
50
  serializer = self.view.get_serializer(translated_page)
51
51
  return Response(serializer.data, status=status.HTTP_201_CREATED)
@@ -43,9 +43,9 @@ class CreatePageAliasAPIAction(APIAction):
43
43
  try:
44
44
  new_page = action.execute()
45
45
  except DjangoValidationError as e:
46
- raise ValidationError(e.message_dict)
46
+ raise ValidationError(e.message_dict) from e
47
47
  except CreatePageAliasIntegrityError as e:
48
- raise BadRequestError(e.args[0])
48
+ raise BadRequestError(e.args[0]) from e
49
49
 
50
50
  serializer = self.view.get_serializer(new_page)
51
51
  return Response(serializer.data, status=status.HTTP_201_CREATED)
@@ -21,6 +21,6 @@ class DeletePageAPIAction(APIAction):
21
21
  try:
22
22
  action.execute()
23
23
  except DjangoValidationError as e:
24
- raise ValidationError(e.message_dict)
24
+ raise ValidationError(e.message_dict) from e
25
25
 
26
26
  return Response(status=status.HTTP_204_NO_CONTENT)
@@ -46,7 +46,7 @@ class MovePageAPIAction(APIAction):
46
46
  try:
47
47
  action.execute()
48
48
  except DjangoValidationError as e:
49
- raise ValidationError(e.message_dict)
49
+ raise ValidationError(e.message_dict) from e
50
50
 
51
51
  instance.refresh_from_db()
52
52
  serializer = self.view.get_serializer(instance)
@@ -22,12 +22,12 @@ class PublishPageAPIAction(APIAction):
22
22
  try:
23
23
  action = self._action_from_data(instance, data)
24
24
  except RuntimeError as e:
25
- raise BadRequestError(e.args[0])
25
+ raise BadRequestError(e.args[0]) from e
26
26
 
27
27
  try:
28
28
  action.execute()
29
29
  except DjangoValidationError as e:
30
- raise ValidationError(e.message_dict)
30
+ raise ValidationError(e.message_dict) from e
31
31
 
32
32
  new_page = instance.specific_class.objects.get(pk=instance.pk)
33
33
  serializer = self.view.get_serializer(new_page)
@@ -34,9 +34,9 @@ class RevertToPageRevisionAPIAction(APIAction):
34
34
  try:
35
35
  new_revision = action.execute()
36
36
  except DjangoValidationError as e:
37
- raise ValidationError(e.message_dict)
37
+ raise ValidationError(e.message_dict) from e
38
38
  except RevertToPageRevisionError as e:
39
- raise BadRequestError(e.args[0])
39
+ raise BadRequestError(e.args[0]) from e
40
40
 
41
41
  serializer = self.view.get_serializer(new_revision.as_object())
42
42
  return Response(serializer.data, status=status.HTTP_200_OK)
@@ -29,7 +29,7 @@ class UnpublishPageAPIAction(APIAction):
29
29
  try:
30
30
  action.execute()
31
31
  except DjangoValidationError as e:
32
- raise ValidationError(e.message_dict)
32
+ raise ValidationError(e.message_dict) from e
33
33
 
34
34
  serializer = self.view.get_serializer(instance)
35
35
  return Response(serializer.data, status=status.HTTP_200_OK)
@@ -15,8 +15,8 @@ class HasChildrenFilter(BaseFilterBackend):
15
15
  if "has_children" in request.GET:
16
16
  try:
17
17
  has_children_filter = parse_boolean(request.GET["has_children"])
18
- except ValueError:
19
- raise BadRequestError("has_children must be 'true' or 'false'")
18
+ except ValueError as e:
19
+ raise BadRequestError("has_children must be 'true' or 'false'") from e
20
20
 
21
21
  if has_children_filter is True:
22
22
  return queryset.filter(numchild__gt=0)
wagtail/admin/compare.py CHANGED
@@ -308,6 +308,28 @@ class StreamBlockComparison(BaseSequenceBlockComparison):
308
308
  def get_block_comparisons(self):
309
309
  return self.get_block_comparisons_by_id()
310
310
 
311
+ def htmlvalue(self, val):
312
+ htmlvalues = []
313
+
314
+ for stream_child in self.get_blocks_from_value(val):
315
+ block = stream_child.block
316
+ label = block.label
317
+ comparison_class = get_comparison_class_for_block(block)
318
+
319
+ htmlvalues.append(
320
+ (
321
+ label,
322
+ comparison_class(
323
+ block, True, True, stream_child.value, stream_child.value
324
+ ).htmlvalue(stream_child.value),
325
+ )
326
+ )
327
+
328
+ return format_html(
329
+ "<dl>\n{}\n</dl>",
330
+ format_html_join("\n", " <dt>{}</dt>\n <dd>{}</dd>", htmlvalues),
331
+ )
332
+
311
333
 
312
334
  class ListBlockComparison(BaseSequenceBlockComparison):
313
335
  @staticmethod
@@ -1,10 +1,14 @@
1
+ import io
2
+ import os
1
3
  import warnings
2
4
 
3
5
  from django import forms
4
6
  from django.conf import settings
5
7
  from django.contrib.auth import get_user_model
8
+ from django.core.files.uploadedfile import InMemoryUploadedFile
6
9
  from django.utils.translation import get_language_info
7
10
  from django.utils.translation import gettext_lazy as _
11
+ from PIL import Image
8
12
 
9
13
  from wagtail.admin.localization import (
10
14
  get_available_admin_languages,
@@ -125,6 +129,53 @@ class AvatarPreferencesForm(forms.ModelForm):
125
129
  super().__init__(*args, **kwargs)
126
130
  self._original_avatar = self.instance.avatar
127
131
 
132
+ def clean_avatar(self):
133
+ file = self.cleaned_data.get("avatar")
134
+ if not file:
135
+ return self._original_avatar
136
+
137
+ image = Image.open(file)
138
+ width, height = image.size
139
+
140
+ if width <= 400 and height <= 400:
141
+ return file
142
+
143
+ target_size = 400
144
+
145
+ if width > height:
146
+ new_width = target_size
147
+ new_height = int(height * (target_size / width))
148
+ else:
149
+ new_height = target_size
150
+ new_width = int(width * (target_size / height))
151
+
152
+ resized_image = image.resize((new_width, new_height))
153
+
154
+ orig_format = image.format or "JPEG"
155
+
156
+ output = io.BytesIO()
157
+ resized_image.save(output, format=image.format)
158
+ output.seek(0)
159
+
160
+ new_ext = (
161
+ "jpg" if orig_format.upper() in ("JPEG", "JPG") else orig_format.lower()
162
+ )
163
+ content_type = f"image/{'jpeg' if new_ext == 'jpg' else new_ext}"
164
+
165
+ base_name, _ = os.path.splitext(file.name)
166
+ filename = f"{base_name}.{new_ext}"
167
+
168
+ new_file = InMemoryUploadedFile(
169
+ file=output,
170
+ field_name="avatar",
171
+ name=filename,
172
+ content_type=content_type,
173
+ size=output.getbuffer().nbytes,
174
+ charset=None,
175
+ )
176
+
177
+ return new_file
178
+
128
179
  def save(self, commit=True):
129
180
  if (
130
181
  commit
@@ -140,7 +191,7 @@ class AvatarPreferencesForm(forms.ModelForm):
140
191
  warnings.warn(
141
192
  "Failed to delete old avatar file: %s" % self._original_avatar.name
142
193
  )
143
- super().save(commit=commit)
194
+ return super().save(commit=commit)
144
195
 
145
196
  class Meta:
146
197
  model = UserProfile
@@ -1,7 +1,11 @@
1
+ from django.contrib.auth import get_user_model
1
2
  from django.forms import BooleanField, ValidationError
2
3
  from django.utils.timezone import now
3
4
  from django.utils.translation import gettext as _
4
5
  from modelcluster.forms import BaseChildFormSet
6
+ from modelcluster.models import get_serializable_data_for_fields
7
+
8
+ from wagtail.admin.templatetags.wagtailadmin_tags import avatar_url, user_display_name
5
9
 
6
10
  from .models import WagtailAdminModelForm
7
11
 
@@ -26,6 +30,11 @@ class CommentReplyForm(WagtailAdminModelForm):
26
30
  )
27
31
  return cleaned_data
28
32
 
33
+ def serialize(self, bound):
34
+ data = get_serializable_data_for_fields(self.instance)
35
+ data["deleted"] = self.cleaned_data.get("DELETE", False) if bound else False
36
+ return data, {self.instance.user_id}
37
+
29
38
 
30
39
  class CommentForm(WagtailAdminModelForm):
31
40
  """
@@ -75,6 +84,24 @@ class CommentForm(WagtailAdminModelForm):
75
84
  self.instance.resolved_at = None
76
85
  return super().save(*args, **kwargs)
77
86
 
87
+ def serialize(self, bound):
88
+ user_pks = {self.instance.user_id}
89
+ replies = []
90
+ for reply_form in self.formsets["replies"].forms:
91
+ reply_data, reply_user_pks = reply_form.serialize(bound)
92
+ replies.append(reply_data)
93
+ user_pks.update(reply_user_pks)
94
+
95
+ data = get_serializable_data_for_fields(self.instance)
96
+ data["deleted"] = self.cleaned_data.get("DELETE", False) if bound else False
97
+ data["resolved"] = (
98
+ self.cleaned_data.get("resolved", False)
99
+ if bound
100
+ else self.instance.resolved_at is not None
101
+ )
102
+ data["replies"] = replies
103
+ return data, user_pks
104
+
78
105
 
79
106
  class CommentFormSet(BaseChildFormSet):
80
107
  def __init__(self, *args, **kwargs):
@@ -85,3 +112,29 @@ class CommentFormSet(BaseChildFormSet):
85
112
  if comment.has_valid_contentpath(self.instance)
86
113
  ]
87
114
  self.queryset = self.queryset.filter(id__in=valid_comment_ids)
115
+
116
+ def serialize(self, bound: bool, user):
117
+ def user_data(user):
118
+ return {"name": user_display_name(user), "avatar_url": avatar_url(user)}
119
+
120
+ user_pks = {user.pk}
121
+ serialized_comments = []
122
+ for form in self.forms:
123
+ # iterate over comments to retrieve users (to get display names) and serialized versions
124
+ data, comment_user_pks = form.serialize(bound)
125
+ serialized_comments.append(data)
126
+ user_pks.update(comment_user_pks)
127
+
128
+ authors = {
129
+ str(user.pk): user_data(user)
130
+ for user in get_user_model()
131
+ .objects.filter(pk__in=user_pks)
132
+ .select_related("wagtail_userprofile")
133
+ }
134
+
135
+ comments_data = {
136
+ "comments": serialized_comments,
137
+ "user": user.pk,
138
+ "authors": authors,
139
+ }
140
+ return comments_data
@@ -170,6 +170,42 @@ class WagtailAdminModelForm(
170
170
  self.fields[field_name].required = True
171
171
  self.deferred_required_fields = []
172
172
 
173
+ def get_field_updates_for_resave(self):
174
+ """
175
+ Following a successful save (as a background HTTP request), returns a list of
176
+ form field updates - as (name, new_value) tuples - that can be applied to the
177
+ form in the still-open page to make it valid for subsequent submissions. This
178
+ includes populating the IDs of child objects within formsets - without this,
179
+ subsequent submissions would create duplicates of these objects.
180
+ """
181
+ updates = []
182
+ for formset in self.formsets.values():
183
+ if formset.total_form_count() != formset.initial_form_count():
184
+ updates.append(
185
+ (
186
+ f"{formset.management_form.prefix}-INITIAL_FORMS",
187
+ str(formset.total_form_count()),
188
+ )
189
+ )
190
+
191
+ for form in formset.forms:
192
+ id_field_name = f"{form.prefix}-id"
193
+
194
+ if formset.can_delete and formset._should_delete_form(form):
195
+ if self.data.get(id_field_name):
196
+ # Form will remain in the formset as a deleted form;
197
+ # clear its ID so that it's skipped over on next submission
198
+ # like an added-and-immediately-deleted form would be
199
+ updates.append((id_field_name, ""))
200
+ else:
201
+ updates.extend(form.get_field_updates_for_resave())
202
+ if form.instance.pk and not self.data.get(id_field_name):
203
+ # instance has a PK but the form data doesn't include it - it must have
204
+ # been created during the save we just performed
205
+ updates.append((id_field_name, str(form.instance.pk)))
206
+
207
+ return updates
208
+
173
209
  class Meta:
174
210
  formfield_callback = formfield_for_dbfield
175
211
 
@@ -205,6 +205,13 @@ class WagtailAdminPageForm(WagtailAdminModelForm):
205
205
  del self.formsets["comments"]
206
206
  return super().is_valid()
207
207
 
208
+ def serialize_comments(self, user):
209
+ if comments := self.formsets.get("comments"):
210
+ data = comments.serialize(self.is_bound, user)
211
+ else:
212
+ data = {"comments": [], "user": user.pk, "authors": {}}
213
+ return data
214
+
208
215
  def clean(self):
209
216
  cleaned_data = super().clean()
210
217
  if "slug" in self.cleaned_data:
@@ -179,7 +179,7 @@ class WorkflowContentTypeForm(forms.Form):
179
179
 
180
180
  def __init__(self, *args, workflow=None, **kwargs):
181
181
  self.workflow = workflow
182
- if workflow and "initial" not in kwargs:
182
+ if workflow and workflow.pk and "initial" not in kwargs:
183
183
  kwargs["initial"] = {"content_types": workflow.workflow_content_types.all()}
184
184
 
185
185
  super().__init__(*args, **kwargs)
@@ -206,7 +206,10 @@ class WorkflowContentTypeForm(forms.Form):
206
206
  existing_assignments = WorkflowContentType.objects.filter(
207
207
  content_type__in=content_types,
208
208
  workflow__active=True,
209
- ).exclude(workflow=self.workflow)
209
+ )
210
+ if self.workflow.pk:
211
+ existing_assignments = existing_assignments.exclude(workflow=self.workflow)
212
+
210
213
  for assignment in existing_assignments:
211
214
  self.add_error(
212
215
  "content_types",
wagtail/admin/icons.py CHANGED
@@ -1,5 +1,4 @@
1
1
  import hashlib
2
- import itertools
3
2
  import re
4
3
  from functools import lru_cache
5
4
 
@@ -13,8 +12,10 @@ icon_comment_pattern = re.compile(r"<!--.*?-->", re.DOTALL)
13
12
 
14
13
  @lru_cache(maxsize=None)
15
14
  def get_icons():
16
- icon_hooks = hooks.get_hooks("register_icons")
17
- all_icons = sorted(itertools.chain.from_iterable(hook([]) for hook in icon_hooks))
15
+ all_icons = []
16
+ for fn in hooks.get_hooks("register_icons"):
17
+ all_icons = fn(all_icons)
18
+ all_icons = sorted(all_icons)
18
19
  combined_icon_markup = ""
19
20
  for icon in all_icons:
20
21
  symbol = (
@@ -89,6 +89,9 @@ msgstr "واجهة إدارة Wagtail"
89
89
  msgid "Sorry, you do not have permission to access this area."
90
90
  msgstr "عذرًا، ليس لديك صلاحية للوصول إلى هذه المنطقة."
91
91
 
92
+ msgid "You do not have permission to access the admin."
93
+ msgstr "ليس لديك صلاحية للوصول لواجهة الإدارة"
94
+
92
95
  msgid "None"
93
96
  msgstr "لا شيء"
94
97
 
@@ -98,12 +101,21 @@ msgstr "التاريخ من"
98
101
  msgid "Date to"
99
102
  msgstr "التاريخ إلى"
100
103
 
104
+ msgid "Minimum"
105
+ msgstr "الحد الأدنى"
106
+
107
+ msgid "Maximum"
108
+ msgstr "الحد الأقصى"
109
+
101
110
  msgid "Locale"
102
111
  msgstr "الإعدادات المحلية"
103
112
 
104
113
  msgid "All"
105
114
  msgstr "الكل"
106
115
 
116
+ msgid "Usage count"
117
+ msgstr "عدد الاستخدام"
118
+
107
119
  msgid "Collection"
108
120
  msgstr "رزمة"
109
121
 
@@ -174,6 +186,9 @@ msgstr ""
174
186
  "اختر الموقع الهرمي. ملاحظة: لا يمكن أن تصبح الرزمة فرعًا لنفسها أو لأحد "
175
187
  "فروعها."
176
188
 
189
+ msgid "Please select another parent."
190
+ msgstr "الرجاء اختيار أصل آخر"
191
+
177
192
  msgid "You cannot have multiple permission records for the same collection."
178
193
  msgstr "لا يمكن أن يكون لديك سجلات صلاحيات متعددة لنفس الرزمة."
179
194
 
@@ -2179,6 +2194,9 @@ msgstr "رجوع"
2179
2194
  msgid "History"
2180
2195
  msgstr "السجل"
2181
2196
 
2197
+ msgid "account"
2198
+ msgstr "الحساب"
2199
+
2182
2200
  msgid "Keyboard shortcuts"
2183
2201
  msgstr "اختصارات لوحة المفاتيح"
2184
2202
 
@@ -3293,6 +3311,9 @@ msgstr "صفحة '%(page_title)s' تم إنشاؤها ونشرها."
3293
3311
  msgid "Page '%(page_title)s' created and submitted for moderation."
3294
3312
  msgstr "صفحة '%(page_title)s' تم إنشاؤها وتقديمها للإشراف."
3295
3313
 
3314
+ msgid "The page could not be created due to validation errors."
3315
+ msgstr "لا يمكن إنشاء الصفحة بسبب أخطاء التحقق من الصحة"
3316
+
3296
3317
  #, python-format
3297
3318
  msgid "Page '%(page_title)s' deleted."
3298
3319
  msgstr "الصفحة '%(page_title)s' محذوفة."
@@ -3350,6 +3371,12 @@ msgstr "الصفحة '%(page_title)s' تم تقديمها للإشراف."
3350
3371
  msgid "Workflow on page '%(page_title)s' has been restarted."
3351
3372
  msgstr "تم إعادة تشغيل سير العمل على الصفحة '%(page_title)s'."
3352
3373
 
3374
+ msgid "The page could not be saved as it is locked."
3375
+ msgstr "لا يمكن حفظ الصفحة لأنها مقفلة"
3376
+
3377
+ msgid "The page could not be saved due to validation errors."
3378
+ msgstr "لا يمكن حفظ الصفحة بسبب أخطاء التحقق من الصحة"
3379
+
3353
3380
  msgid "Is commenting action"
3354
3381
  msgstr "إجراء التعليق"
3355
3382
 
@@ -3384,6 +3411,14 @@ msgstr "استكشاف"
3384
3411
  msgid "Page '%(page_title)s' is now unlocked."
3385
3412
  msgstr "الصفحة '%(page_title)s' غير مقفلة الآن."
3386
3413
 
3414
+ #, python-format
3415
+ msgid ""
3416
+ "The slug '%(page_slug)s' is already in use at the selected parent page. Make "
3417
+ "sure the slug is unique and try again."
3418
+ msgstr ""
3419
+ "الرابط '%(page_slug)s' مستخدم بالفعل في الصفحة الأصل المحددة. تأكد من أن "
3420
+ "الرابط فريد وحاول مرة أخرى"
3421
+
3387
3422
  #, python-format
3388
3423
  msgid "Page '%(page_title)s' moved."
3389
3424
  msgstr "تم نقل الصفحة '%(page_title)s'."