wagtail 6.2.2__py3-none-any.whl → 6.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 (386) hide show
  1. wagtail/__init__.py +1 -1
  2. wagtail/actions/copy_for_translation.py +6 -0
  3. wagtail/actions/publish_revision.py +3 -3
  4. wagtail/admin/action_menu.py +5 -3
  5. wagtail/admin/forms/account.py +1 -1
  6. wagtail/admin/icons.py +2 -6
  7. wagtail/admin/locale/cy/LC_MESSAGES/django.mo +0 -0
  8. wagtail/admin/locale/cy/LC_MESSAGES/django.po +32 -0
  9. wagtail/admin/locale/dv/LC_MESSAGES/django.mo +0 -0
  10. wagtail/admin/locale/dv/LC_MESSAGES/django.po +28 -0
  11. wagtail/admin/locale/en/LC_MESSAGES/django.po +451 -485
  12. wagtail/admin/locale/en/LC_MESSAGES/djangojs.po +7 -7
  13. wagtail/admin/locale/sl/LC_MESSAGES/django.mo +0 -0
  14. wagtail/admin/locale/sl/LC_MESSAGES/django.po +150 -0
  15. wagtail/admin/locale/sl/LC_MESSAGES/djangojs.mo +0 -0
  16. wagtail/admin/locale/sl/LC_MESSAGES/djangojs.po +9 -2
  17. wagtail/admin/locale/ug/LC_MESSAGES/django.mo +0 -0
  18. wagtail/admin/locale/ug/LC_MESSAGES/django.po +3250 -196
  19. wagtail/admin/localization.py +8 -2
  20. wagtail/admin/panels/model_utils.py +1 -1
  21. wagtail/admin/site_summary.py +0 -2
  22. wagtail/admin/static/wagtailadmin/css/core.css +1 -1
  23. wagtail/admin/static/wagtailadmin/css/panels/draftail.css +1 -1
  24. wagtail/admin/static/wagtailadmin/images/email-header.jpg +0 -0
  25. wagtail/admin/static/wagtailadmin/js/bulk-actions.js +1 -1
  26. wagtail/admin/static/wagtailadmin/js/core.js +1 -1
  27. wagtail/admin/static/wagtailadmin/js/core.js.LICENSE.txt +12 -0
  28. wagtail/admin/static/wagtailadmin/js/draftail.js +1 -1
  29. wagtail/admin/static/wagtailadmin/js/sidebar.js +1 -1
  30. wagtail/admin/static/wagtailadmin/js/telepath/telepath.js +1 -1
  31. wagtail/admin/static/wagtailadmin/js/telepath/widgets.js +1 -1
  32. wagtail/admin/static/wagtailadmin/js/userbar.js +1 -1
  33. wagtail/admin/static/wagtailadmin/js/userbar.js.LICENSE.txt +1 -1
  34. wagtail/admin/static/wagtailadmin/js/vendor.js +1 -1
  35. wagtail/admin/static/wagtailadmin/js/wagtailadmin.js +1 -1
  36. wagtail/admin/static/wagtailadmin/js/workflow-action.js +1 -1
  37. wagtail/admin/staticfiles.py +6 -4
  38. wagtail/admin/templates/wagtailadmin/account/account.html +42 -59
  39. wagtail/admin/templates/wagtailadmin/admin_base.html +0 -28
  40. wagtail/admin/templates/wagtailadmin/bulk_actions/footer.html +1 -1
  41. wagtail/admin/templates/wagtailadmin/chooser/_search_results.html +7 -5
  42. wagtail/admin/templates/wagtailadmin/chooser/tables/page_navigate_to_children_cell.html +1 -1
  43. wagtail/admin/templates/wagtailadmin/generic/chooser/results.html +7 -5
  44. wagtail/admin/templates/wagtailadmin/generic/edit.html +0 -8
  45. wagtail/admin/templates/wagtailadmin/generic/form.html +60 -17
  46. wagtail/admin/templates/wagtailadmin/generic/index.html +17 -0
  47. wagtail/admin/templates/wagtailadmin/generic/index_results.html +9 -35
  48. wagtail/admin/templates/wagtailadmin/generic/inspect.html +2 -21
  49. wagtail/admin/templates/wagtailadmin/generic/listing.html +11 -17
  50. wagtail/admin/templates/wagtailadmin/generic/listing_results.html +19 -1
  51. wagtail/admin/templates/wagtailadmin/home/account_summary.html +26 -0
  52. wagtail/admin/templates/wagtailadmin/home/locked_pages.html +28 -18
  53. wagtail/admin/templates/wagtailadmin/home/recent_edits.html +28 -17
  54. wagtail/admin/templates/wagtailadmin/home/site_summary_pages.html +2 -2
  55. wagtail/admin/templates/wagtailadmin/home/upgrade_notification.html +35 -13
  56. wagtail/admin/templates/wagtailadmin/home/user_objects_in_workflow_moderation.html +28 -18
  57. wagtail/admin/templates/wagtailadmin/home/workflow_objects_to_moderate.html +43 -32
  58. wagtail/admin/templates/wagtailadmin/home.html +22 -5
  59. wagtail/admin/templates/wagtailadmin/pages/_editor_js.html +0 -1
  60. wagtail/admin/templates/wagtailadmin/pages/action_menu/menu.html +1 -11
  61. wagtail/admin/templates/wagtailadmin/pages/action_menu/menu_item.html +1 -6
  62. wagtail/admin/templates/wagtailadmin/pages/action_menu/page_locked.html +1 -1
  63. wagtail/admin/templates/wagtailadmin/pages/action_menu/publish.html +1 -1
  64. wagtail/admin/templates/wagtailadmin/pages/action_menu/save_draft.html +1 -1
  65. wagtail/admin/templates/wagtailadmin/pages/bulk_actions/confirm_bulk_delete.html +12 -6
  66. wagtail/admin/templates/wagtailadmin/pages/bulk_actions/confirm_bulk_move.html +12 -7
  67. wagtail/admin/templates/wagtailadmin/pages/bulk_actions/confirm_bulk_publish.html +17 -11
  68. wagtail/admin/templates/wagtailadmin/pages/bulk_actions/confirm_bulk_unpublish.html +15 -9
  69. wagtail/admin/templates/wagtailadmin/pages/confirm_delete.html +9 -9
  70. wagtail/admin/templates/wagtailadmin/pages/confirm_move.html +2 -2
  71. wagtail/admin/templates/wagtailadmin/pages/confirm_unpublish.html +4 -4
  72. wagtail/admin/templates/wagtailadmin/pages/create.html +15 -34
  73. wagtail/admin/templates/wagtailadmin/pages/edit.html +15 -30
  74. wagtail/admin/templates/wagtailadmin/pages/explorable_index.html +6 -0
  75. wagtail/admin/templates/wagtailadmin/pages/explorable_index_results.html +60 -0
  76. wagtail/admin/templates/wagtailadmin/pages/index.html +1 -36
  77. wagtail/admin/templates/wagtailadmin/pages/index_results.html +2 -50
  78. wagtail/admin/templates/wagtailadmin/pages/listing/_locked_indicator.html +1 -1
  79. wagtail/admin/templates/wagtailadmin/pages/listing/_ordering_cell.html +1 -1
  80. wagtail/admin/templates/wagtailadmin/pages/listing/_page_header_buttons.html +1 -1
  81. wagtail/admin/templates/wagtailadmin/pages/listing/_page_title_column_header.html +3 -3
  82. wagtail/admin/templates/wagtailadmin/pages/listing/_pagination.html +1 -1
  83. wagtail/admin/templates/wagtailadmin/pages/listing.html +24 -0
  84. wagtail/admin/templates/wagtailadmin/pages/search.html +1 -20
  85. wagtail/admin/templates/wagtailadmin/pages/search_results.html +27 -25
  86. wagtail/admin/templates/wagtailadmin/permissions/includes/collection_member_permissions_formset.html +0 -1
  87. wagtail/admin/templates/wagtailadmin/reports/listing/_list_page_report.html +2 -2
  88. wagtail/admin/templates/wagtailadmin/reports/listing/_list_page_types_usage.html +6 -6
  89. wagtail/admin/templates/wagtailadmin/reports/workflow_tasks_results.html +1 -1
  90. wagtail/admin/templates/wagtailadmin/shared/action_menu/menu.html +14 -0
  91. wagtail/admin/templates/wagtailadmin/shared/action_menu/menu_item.html +6 -0
  92. wagtail/admin/templates/wagtailadmin/shared/avatar.html +13 -3
  93. wagtail/admin/templates/wagtailadmin/shared/header.html +0 -5
  94. wagtail/admin/templates/wagtailadmin/shared/headers/slim_header.html +1 -1
  95. wagtail/admin/templates/wagtailadmin/shared/page_status_tag_new.html +4 -1
  96. wagtail/admin/templates/wagtailadmin/shared/pagination_nav.html +1 -1
  97. wagtail/admin/templates/wagtailadmin/shared/side_panel_toggle.html +1 -1
  98. wagtail/admin/templates/wagtailadmin/shared/side_panels/includes/status/locale.html +1 -1
  99. wagtail/admin/templates/wagtailadmin/shared/side_panels/includes/status/usage.html +2 -2
  100. wagtail/admin/templates/wagtailadmin/shared/side_panels/preview.html +94 -52
  101. wagtail/admin/templates/wagtailadmin/{pages/_unsaved_changes_warning.html → shared/unsaved_changes_warning.html} +4 -4
  102. wagtail/admin/templates/wagtailadmin/shared/usage_summary.html +2 -2
  103. wagtail/admin/templates/wagtailadmin/shared/workflow_history/detail.html +1 -7
  104. wagtail/admin/templates/wagtailadmin/shared/workflow_history/listing.html +1 -0
  105. wagtail/admin/templates/wagtailadmin/shared/workflow_history/{list.html → listing_results.html} +33 -35
  106. wagtail/admin/templates/wagtailadmin/tables/references_cell.html +1 -1
  107. wagtail/admin/templates/wagtailadmin/workflows/create.html +11 -36
  108. wagtail/admin/templates/wagtailadmin/workflows/create_task.html +0 -38
  109. wagtail/admin/templates/wagtailadmin/workflows/edit.html +44 -74
  110. wagtail/admin/templates/wagtailadmin/workflows/edit_task.html +27 -66
  111. wagtail/admin/templates/wagtailadmin/workflows/includes/task_usage_cell.html +4 -2
  112. wagtail/admin/templates/wagtailadmin/workflows/includes/workflow_tasks_cell.html +4 -4
  113. wagtail/admin/templates/wagtailadmin/workflows/includes/workflow_used_by_cell.html +15 -11
  114. wagtail/admin/templates/wagtailadmin/workflows/task_chooser/includes/results.html +7 -5
  115. wagtail/admin/templatetags/wagtailadmin_tags.py +51 -22
  116. wagtail/admin/tests/pages/test_content_type_use_view.py +82 -2
  117. wagtail/admin/tests/pages/test_edit_page.py +70 -0
  118. wagtail/admin/tests/pages/test_explorer_view.py +65 -4
  119. wagtail/admin/tests/pages/test_page_search.py +8 -7
  120. wagtail/admin/tests/pages/test_page_usage.py +3 -1
  121. wagtail/admin/tests/pages/test_preview.py +208 -63
  122. wagtail/admin/tests/pages/test_reorder_page.py +91 -1
  123. wagtail/admin/tests/pages/test_view_draft.py +32 -1
  124. wagtail/admin/tests/pages/test_workflow_history.py +288 -7
  125. wagtail/admin/tests/test_account_management.py +23 -3
  126. wagtail/admin/tests/test_audit_log.py +24 -2
  127. wagtail/admin/tests/test_collections_views.py +17 -5
  128. wagtail/admin/tests/test_dashboard.py +1 -1
  129. wagtail/admin/tests/test_edit_handlers.py +3 -2
  130. wagtail/admin/tests/test_icon_sprite.py +4 -0
  131. wagtail/admin/tests/test_privacy.py +5 -19
  132. wagtail/admin/tests/test_reports_views.py +1 -1
  133. wagtail/admin/tests/test_site_summary.py +3 -3
  134. wagtail/admin/tests/test_templatetags.py +27 -3
  135. wagtail/admin/tests/test_upgrade_notification.py +71 -18
  136. wagtail/admin/tests/test_views.py +2 -2
  137. wagtail/admin/tests/test_views_generic.py +13 -0
  138. wagtail/admin/tests/test_widgets.py +3 -3
  139. wagtail/admin/tests/test_workflows.py +172 -27
  140. wagtail/admin/tests/tests.py +1 -1
  141. wagtail/admin/tests/viewsets/test_model_viewset.py +55 -3
  142. wagtail/admin/ui/side_panels.py +19 -0
  143. wagtail/admin/ui/sidebar.py +4 -3
  144. wagtail/admin/ui/tables/__init__.py +14 -1
  145. wagtail/admin/ui/tables/pages.py +6 -1
  146. wagtail/admin/urls/pages.py +10 -1
  147. wagtail/admin/urls/workflows.py +6 -1
  148. wagtail/admin/views/account.py +5 -1
  149. wagtail/admin/views/collections.py +0 -2
  150. wagtail/admin/views/generic/base.py +118 -1
  151. wagtail/admin/views/generic/history.py +158 -26
  152. wagtail/admin/views/generic/models.py +113 -99
  153. wagtail/admin/views/home.py +24 -9
  154. wagtail/admin/views/page_privacy.py +54 -30
  155. wagtail/admin/views/pages/history.py +11 -4
  156. wagtail/admin/views/pages/listing.py +76 -89
  157. wagtail/admin/views/pages/preview.py +1 -1
  158. wagtail/admin/views/pages/search.py +27 -71
  159. wagtail/admin/views/pages/usage.py +63 -24
  160. wagtail/admin/views/pages/utils.py +4 -3
  161. wagtail/admin/views/reports/base.py +0 -6
  162. wagtail/admin/views/workflows.py +67 -25
  163. wagtail/admin/viewsets/model.py +4 -3
  164. wagtail/admin/widgets/chooser.py +2 -1
  165. wagtail/api/v2/tests/test_pages.py +15 -2
  166. wagtail/blocks/field_block.py +15 -3
  167. wagtail/blocks/static_block.py +3 -0
  168. wagtail/contrib/forms/locale/cy/LC_MESSAGES/django.mo +0 -0
  169. wagtail/contrib/forms/locale/cy/LC_MESSAGES/django.po +29 -1
  170. wagtail/contrib/forms/locale/en/LC_MESSAGES/django.po +15 -11
  171. wagtail/contrib/forms/templates/wagtailforms/confirm_delete.html +1 -0
  172. wagtail/contrib/forms/templates/wagtailforms/index.html +1 -1
  173. wagtail/contrib/forms/templates/wagtailforms/index_results.html +1 -1
  174. wagtail/contrib/forms/templates/wagtailforms/list_submissions.html +2 -1
  175. wagtail/contrib/forms/templates/wagtailforms/panels/form_responses_panel.html +2 -2
  176. wagtail/contrib/forms/tests/test_views.py +234 -35
  177. wagtail/contrib/forms/utils.py +8 -14
  178. wagtail/contrib/forms/views.py +72 -27
  179. wagtail/contrib/frontend_cache/backends/azure.py +1 -1
  180. wagtail/contrib/frontend_cache/backends/cloudfront.py +2 -2
  181. wagtail/contrib/frontend_cache/backends/dummy.py +11 -0
  182. wagtail/contrib/frontend_cache/tests.py +31 -37
  183. wagtail/contrib/frontend_cache/utils.py +12 -5
  184. wagtail/contrib/redirects/locale/cy/LC_MESSAGES/django.mo +0 -0
  185. wagtail/contrib/redirects/locale/cy/LC_MESSAGES/django.po +16 -1
  186. wagtail/contrib/redirects/locale/en/LC_MESSAGES/django.po +20 -24
  187. wagtail/contrib/redirects/models.py +16 -1
  188. wagtail/contrib/redirects/signal_handlers.py +18 -3
  189. wagtail/contrib/redirects/templates/wagtailredirects/add.html +5 -16
  190. wagtail/contrib/redirects/templates/wagtailredirects/import_summary.html +1 -1
  191. wagtail/contrib/redirects/tests/test_redirects.py +49 -6
  192. wagtail/contrib/redirects/tests/test_signal_handlers.py +42 -1
  193. wagtail/contrib/redirects/views.py +27 -3
  194. wagtail/contrib/search_promotions/locale/cy/LC_MESSAGES/django.mo +0 -0
  195. wagtail/contrib/search_promotions/locale/cy/LC_MESSAGES/django.po +60 -1
  196. wagtail/contrib/search_promotions/locale/en/LC_MESSAGES/django.po +12 -18
  197. wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/add.html +5 -8
  198. wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/edit.html +15 -10
  199. wagtail/contrib/search_promotions/tests.py +13 -2
  200. wagtail/contrib/search_promotions/views.py +15 -3
  201. wagtail/contrib/settings/locale/cy/LC_MESSAGES/django.mo +0 -0
  202. wagtail/contrib/settings/locale/cy/LC_MESSAGES/django.po +6 -1
  203. wagtail/contrib/settings/locale/en/LC_MESSAGES/django.po +2 -10
  204. wagtail/contrib/settings/templates/wagtailsettings/edit.html +12 -45
  205. wagtail/contrib/simple_translation/locale/cy/LC_MESSAGES/django.mo +0 -0
  206. wagtail/contrib/simple_translation/locale/cy/LC_MESSAGES/django.po +13 -1
  207. wagtail/contrib/simple_translation/locale/dv/LC_MESSAGES/django.mo +0 -0
  208. wagtail/contrib/simple_translation/locale/dv/LC_MESSAGES/django.po +3 -0
  209. wagtail/contrib/simple_translation/locale/en/LC_MESSAGES/django.po +4 -4
  210. wagtail/contrib/simple_translation/locale/sl/LC_MESSAGES/django.mo +0 -0
  211. wagtail/contrib/simple_translation/locale/sl/LC_MESSAGES/django.po +5 -2
  212. wagtail/contrib/simple_translation/tests/test_views.py +51 -0
  213. wagtail/contrib/simple_translation/wagtail_hooks.py +16 -11
  214. wagtail/contrib/styleguide/locale/cy/LC_MESSAGES/django.mo +0 -0
  215. wagtail/contrib/styleguide/locale/cy/LC_MESSAGES/django.po +5 -1
  216. wagtail/contrib/styleguide/locale/en/LC_MESSAGES/django.po +1 -1
  217. wagtail/contrib/table_block/locale/cy/LC_MESSAGES/django.mo +0 -0
  218. wagtail/contrib/table_block/locale/cy/LC_MESSAGES/django.po +27 -1
  219. wagtail/contrib/table_block/locale/en/LC_MESSAGES/django.po +1 -1
  220. wagtail/contrib/typed_table_block/blocks.py +25 -0
  221. wagtail/contrib/typed_table_block/locale/cy/LC_MESSAGES/django.mo +0 -0
  222. wagtail/contrib/typed_table_block/locale/cy/LC_MESSAGES/django.po +12 -1
  223. wagtail/contrib/typed_table_block/locale/en/LC_MESSAGES/django.po +10 -10
  224. wagtail/contrib/typed_table_block/tests.py +24 -1
  225. wagtail/coreutils.py +5 -11
  226. wagtail/documents/admin_urls.py +2 -2
  227. wagtail/documents/locale/cy/LC_MESSAGES/django.mo +0 -0
  228. wagtail/documents/locale/cy/LC_MESSAGES/django.po +10 -0
  229. wagtail/documents/locale/en/LC_MESSAGES/django.po +61 -76
  230. wagtail/documents/migrations/0014_alter_document_file_size.py +18 -0
  231. wagtail/documents/models.py +1 -1
  232. wagtail/documents/rich_text/__init__.py +1 -3
  233. wagtail/documents/static/wagtaildocs/js/add-multiple.js +1 -1
  234. wagtail/documents/templates/wagtaildocs/bulk_actions/confirm_bulk_add_tags.html +5 -1
  235. wagtail/documents/templates/wagtaildocs/bulk_actions/confirm_bulk_add_to_collection.html +5 -1
  236. wagtail/documents/templates/wagtaildocs/bulk_actions/confirm_bulk_delete.html +11 -5
  237. wagtail/documents/templates/wagtaildocs/documents/add.html +13 -41
  238. wagtail/documents/templates/wagtaildocs/documents/edit.html +28 -56
  239. wagtail/documents/templates/wagtaildocs/documents/index.html +1 -4
  240. wagtail/documents/templates/wagtaildocs/homepage/site_summary_documents.html +2 -2
  241. wagtail/documents/templates/wagtaildocs/multiple/add.html +36 -41
  242. wagtail/documents/tests/test_admin_views.py +64 -6
  243. wagtail/documents/tests/test_site_summary.py +3 -3
  244. wagtail/documents/views/documents.py +103 -113
  245. wagtail/documents/views/multiple.py +19 -1
  246. wagtail/embeds/locale/en/LC_MESSAGES/django.po +1 -1
  247. wagtail/embeds/oembed_providers.py +9 -19
  248. wagtail/fields.py +43 -27
  249. wagtail/images/admin_urls.py +2 -2
  250. wagtail/images/blocks.py +230 -2
  251. wagtail/images/fields.py +17 -29
  252. wagtail/images/image_operations.py +1 -1
  253. wagtail/images/locale/cy/LC_MESSAGES/django.mo +0 -0
  254. wagtail/images/locale/cy/LC_MESSAGES/django.po +128 -1
  255. wagtail/images/locale/en/LC_MESSAGES/django.po +119 -129
  256. wagtail/images/migrations/0025_alter_image_file_alter_rendition_file.py +36 -43
  257. wagtail/images/migrations/0027_image_description.py +20 -0
  258. wagtail/images/models.py +120 -45
  259. wagtail/images/rich_text/__init__.py +1 -3
  260. wagtail/images/static/wagtailimages/js/add-multiple.js +1 -1
  261. wagtail/images/static/wagtailimages/js/image-block.js +1 -0
  262. wagtail/images/templates/wagtailimages/bulk_actions/confirm_bulk_add_tags.html +5 -1
  263. wagtail/images/templates/wagtailimages/bulk_actions/confirm_bulk_add_to_collection.html +5 -1
  264. wagtail/images/templates/wagtailimages/bulk_actions/confirm_bulk_delete.html +11 -5
  265. wagtail/images/templates/wagtailimages/chooser/results.html +2 -2
  266. wagtail/images/templates/wagtailimages/homepage/site_summary_images.html +2 -2
  267. wagtail/images/templates/wagtailimages/images/_file_field.html +2 -2
  268. wagtail/images/templates/wagtailimages/images/add.html +13 -31
  269. wagtail/images/templates/wagtailimages/images/edit.html +53 -82
  270. wagtail/images/templates/wagtailimages/images/index.html +1 -4
  271. wagtail/images/templates/wagtailimages/images/url_generator.html +1 -1
  272. wagtail/images/templates/wagtailimages/multiple/add.html +40 -47
  273. wagtail/images/templates/wagtailimages/widgets/image.html +5 -0
  274. wagtail/images/templates/wagtailimages/widgets/image_chooser.html +1 -1
  275. wagtail/images/tests/test_admin_views.py +70 -10
  276. wagtail/images/tests/test_blocks.py +367 -1
  277. wagtail/images/tests/test_image_operations.py +23 -0
  278. wagtail/images/tests/test_models.py +20 -0
  279. wagtail/images/tests/test_signal_handlers.py +99 -95
  280. wagtail/images/tests/test_site_summary.py +3 -3
  281. wagtail/images/tests/test_templatetags.py +11 -7
  282. wagtail/images/tests/tests.py +4 -0
  283. wagtail/images/views/images.py +103 -104
  284. wagtail/images/views/multiple.py +17 -1
  285. wagtail/locale/cy/LC_MESSAGES/django.mo +0 -0
  286. wagtail/locale/cy/LC_MESSAGES/django.po +3 -0
  287. wagtail/locale/en/LC_MESSAGES/django.po +137 -125
  288. wagtail/locale/sl/LC_MESSAGES/django.mo +0 -0
  289. wagtail/locale/sl/LC_MESSAGES/django.po +3 -0
  290. wagtail/locales/locale/en/LC_MESSAGES/django.po +8 -8
  291. wagtail/locales/views.py +4 -1
  292. wagtail/migrations/0089_log_entry_data_json_null_to_object.py +1 -7
  293. wagtail/models/__init__.py +122 -14
  294. wagtail/models/audit_log.py +1 -3
  295. wagtail/models/i18n.py +1 -2
  296. wagtail/project_template/Dockerfile +3 -3
  297. wagtail/project_template/requirements.txt +2 -2
  298. wagtail/query.py +17 -4
  299. wagtail/rich_text/__init__.py +2 -3
  300. wagtail/rich_text/pages.py +2 -4
  301. wagtail/rich_text/rewriters.py +2 -2
  302. wagtail/search/backends/database/mysql/query.py +3 -3
  303. wagtail/search/backends/database/sqlite/query.py +6 -6
  304. wagtail/search/locale/en/LC_MESSAGES/django.po +1 -1
  305. wagtail/sites/locale/en/LC_MESSAGES/django.po +6 -6
  306. wagtail/sites/views.py +0 -1
  307. wagtail/snippets/action_menu.py +14 -4
  308. wagtail/snippets/locale/cy/LC_MESSAGES/django.mo +0 -0
  309. wagtail/snippets/locale/cy/LC_MESSAGES/django.po +73 -1
  310. wagtail/snippets/locale/en/LC_MESSAGES/django.po +27 -33
  311. wagtail/snippets/static/wagtailsnippets/js/snippet-chooser-telepath.js +1 -1
  312. wagtail/snippets/static/wagtailsnippets/js/snippet-chooser.js +1 -1
  313. wagtail/snippets/templates/wagtailsnippets/bulk_actions/confirm_bulk_delete.html +5 -3
  314. wagtail/snippets/templates/wagtailsnippets/snippets/action_menu/locked.html +1 -1
  315. wagtail/snippets/templates/wagtailsnippets/snippets/action_menu/menu.html +1 -11
  316. wagtail/snippets/templates/wagtailsnippets/snippets/action_menu/menu_item.html +1 -6
  317. wagtail/snippets/templates/wagtailsnippets/snippets/action_menu/publish.html +1 -1
  318. wagtail/snippets/templates/wagtailsnippets/snippets/action_menu/save.html +1 -1
  319. wagtail/snippets/templates/wagtailsnippets/snippets/create.html +1 -18
  320. wagtail/snippets/templates/wagtailsnippets/snippets/edit.html +1 -14
  321. wagtail/snippets/templates/wagtailsnippets/snippets/index.html +2 -5
  322. wagtail/snippets/tests/test_preview.py +193 -61
  323. wagtail/snippets/tests/test_snippets.py +247 -38
  324. wagtail/snippets/tests/test_usage.py +5 -0
  325. wagtail/snippets/tests/test_viewset.py +25 -9
  326. wagtail/snippets/tests/test_workflows.py +232 -19
  327. wagtail/snippets/views/snippets.py +1 -10
  328. wagtail/test/numberformat.py +104 -0
  329. wagtail/test/settings.py +10 -0
  330. wagtail/test/settings_ui.py +2 -0
  331. wagtail/test/testapp/migrations/0010_alter_customimage_file_and_more.py +71 -78
  332. wagtail/test/testapp/migrations/0040_nocreatablesubpagetypespage_nosubpagetypespage.py +54 -0
  333. wagtail/test/testapp/migrations/0041_alter_jsonstreammodel_options.py +17 -0
  334. wagtail/test/testapp/migrations/0042_alter_customdocument_file_size_and_more.py +28 -0
  335. wagtail/test/testapp/migrations/0043_customimage_description.py +41 -0
  336. wagtail/test/testapp/migrations/0044_custompreviewsizesmodel_custompreviewsizespage.py +52 -0
  337. wagtail/test/testapp/migrations/0045_alter_streampage_body.py +52 -0
  338. wagtail/test/testapp/models.py +62 -4
  339. wagtail/test/testapp/rich_text.py +2 -2
  340. wagtail/test/testapp/templates/tests/form_page_landing.html +2 -1
  341. wagtail/test/testapp/urls.py +5 -0
  342. wagtail/test/testapp/views.py +5 -0
  343. wagtail/test/utils/page_tests.py +5 -5
  344. wagtail/test/utils/template_tests.py +2 -2
  345. wagtail/tests/test_blocks.py +15 -0
  346. wagtail/tests/test_page_permissions.py +109 -0
  347. wagtail/tests/test_revision_model.py +27 -0
  348. wagtail/tests/test_signals.py +21 -2
  349. wagtail/tests/test_tests.py +30 -0
  350. wagtail/users/locale/cy/LC_MESSAGES/django.mo +0 -0
  351. wagtail/users/locale/cy/LC_MESSAGES/django.po +118 -1
  352. wagtail/users/locale/dv/LC_MESSAGES/django.mo +0 -0
  353. wagtail/users/locale/dv/LC_MESSAGES/django.po +3 -0
  354. wagtail/users/locale/en/LC_MESSAGES/django.po +89 -113
  355. wagtail/users/locale/sl/LC_MESSAGES/django.mo +0 -0
  356. wagtail/users/locale/sl/LC_MESSAGES/django.po +3 -0
  357. wagtail/users/migrations/0014_userprofile_contrast.py +23 -0
  358. wagtail/users/models.py +11 -0
  359. wagtail/users/templates/wagtailusers/bulk_actions/confirm_bulk_assign_role.html +5 -1
  360. wagtail/users/templates/wagtailusers/bulk_actions/confirm_bulk_delete.html +5 -1
  361. wagtail/users/templates/wagtailusers/bulk_actions/confirm_bulk_set_active_state.html +5 -1
  362. wagtail/users/templates/wagtailusers/groups/create.html +19 -17
  363. wagtail/users/templates/wagtailusers/groups/edit.html +2 -40
  364. wagtail/users/templates/wagtailusers/groups/includes/formatted_permissions.html +1 -62
  365. wagtail/users/templates/wagtailusers/groups/includes/page_permissions_formset.html +0 -1
  366. wagtail/users/templates/wagtailusers/users/create.html +46 -50
  367. wagtail/users/templates/wagtailusers/users/edit.html +45 -62
  368. wagtail/users/templates/wagtailusers/users/index.html +1 -4
  369. wagtail/users/templatetags/wagtailusers_tags.py +1 -5
  370. wagtail/users/tests/test_admin_views.py +85 -66
  371. wagtail/users/views/groups.py +4 -1
  372. wagtail/users/views/users.py +2 -0
  373. {wagtail-6.2.2.dist-info → wagtail-6.3rc1.dist-info}/METADATA +6 -6
  374. {wagtail-6.2.2.dist-info → wagtail-6.3rc1.dist-info}/RECORD +378 -367
  375. wagtail/admin/static/wagtailadmin/js/preview-panel.js +0 -2
  376. wagtail/admin/static/wagtailadmin/js/preview-panel.js.LICENSE.txt +0 -11
  377. wagtail/admin/templates/wagtailadmin/generic/history_results.html +0 -1
  378. wagtail/admin/templates/wagtailadmin/page_privacy/ancestor_privacy.html +0 -3
  379. wagtail/admin/templates/wagtailadmin/pages/usage_results.html +0 -6
  380. wagtail/admin/templates/wagtailadmin/shared/workflow_history/index.html +0 -17
  381. wagtail/admin/templates/wagtailadmin/shared/workflow_history/results.html +0 -17
  382. wagtail/admin/templates/wagtailadmin/workflows/usage.html +0 -44
  383. {wagtail-6.2.2.dist-info → wagtail-6.3rc1.dist-info}/LICENSE +0 -0
  384. {wagtail-6.2.2.dist-info → wagtail-6.3rc1.dist-info}/WHEEL +0 -0
  385. {wagtail-6.2.2.dist-info → wagtail-6.3rc1.dist-info}/entry_points.txt +0 -0
  386. {wagtail-6.2.2.dist-info → wagtail-6.3rc1.dist-info}/top_level.txt +0 -0
@@ -2,8 +2,15 @@ import unittest.mock
2
2
 
3
3
  from django.apps import apps
4
4
  from django.test import TestCase
5
+ from django.utils.safestring import SafeString
5
6
 
6
- from wagtail.images.blocks import ImageChooserBlock
7
+ from wagtail.admin import compare
8
+ from wagtail.blocks.stream_block import StreamValue
9
+ from wagtail.blocks.struct_block import StructBlockValidationError
10
+ from wagtail.images.blocks import ImageBlock, ImageChooserBlock
11
+ from wagtail.telepath import JSContext
12
+ from wagtail.test.testapp.models import StreamPage
13
+ from wagtail.test.utils.wagtail_tests import WagtailTestUtils
7
14
 
8
15
  from .utils import (
9
16
  Image,
@@ -73,3 +80,362 @@ class TestImageChooserBlock(TestCase):
73
80
 
74
81
  # None should not yield any references
75
82
  self.assertListEqual(list(block.extract_references(None)), [])
83
+
84
+
85
+ class TestImageChooserBlockComparison(TestCase):
86
+ comparison_class = compare.StreamFieldComparison
87
+
88
+ def setUp(self):
89
+ self.image_1 = Image.objects.create(
90
+ title="Test image 1",
91
+ file=get_test_image_file(),
92
+ )
93
+
94
+ self.image_2 = Image.objects.create(
95
+ title="Test image 2",
96
+ file=get_test_image_file(),
97
+ )
98
+
99
+ self.field = StreamPage._meta.get_field("body")
100
+
101
+ def test_hasnt_changed(self):
102
+ field = StreamPage._meta.get_field("body")
103
+
104
+ comparison = self.comparison_class(
105
+ field,
106
+ StreamPage(
107
+ body=StreamValue(
108
+ field.stream_block,
109
+ [
110
+ ("image", self.image_1, "1"),
111
+ ],
112
+ )
113
+ ),
114
+ StreamPage(
115
+ body=StreamValue(
116
+ field.stream_block,
117
+ [
118
+ ("image", self.image_1, "1"),
119
+ ],
120
+ )
121
+ ),
122
+ )
123
+
124
+ self.assertTrue(comparison.is_field)
125
+ self.assertFalse(comparison.is_child_relation)
126
+ self.assertEqual(comparison.field_label(), "Body")
127
+ htmldiff = comparison.htmldiff()
128
+ self.assertIsInstance(htmldiff, SafeString)
129
+ self.assertIn('class="comparison__child-object"', htmldiff)
130
+ self.assertIn('class="preview-image"', htmldiff)
131
+ self.assertNotIn("deletion", htmldiff)
132
+ self.assertNotIn("addition", htmldiff)
133
+ self.assertFalse(comparison.has_changed())
134
+
135
+ def test_has_changed(self):
136
+ field = StreamPage._meta.get_field("body")
137
+
138
+ comparison = self.comparison_class(
139
+ field,
140
+ StreamPage(
141
+ body=StreamValue(
142
+ field.stream_block,
143
+ [
144
+ ("image", self.image_1, "1"),
145
+ ],
146
+ )
147
+ ),
148
+ StreamPage(
149
+ body=StreamValue(
150
+ field.stream_block,
151
+ [
152
+ ("image", self.image_2, "1"),
153
+ ],
154
+ )
155
+ ),
156
+ )
157
+
158
+ self.assertTrue(comparison.is_field)
159
+ self.assertFalse(comparison.is_child_relation)
160
+ self.assertEqual(comparison.field_label(), "Body")
161
+ htmldiff = comparison.htmldiff()
162
+ self.assertIsInstance(htmldiff, SafeString)
163
+ self.assertIn('class="comparison__child-object"', htmldiff)
164
+ self.assertIn('class="preview-image deletion"', htmldiff)
165
+ self.assertIn('class="preview-image addition"', htmldiff)
166
+ self.assertTrue(comparison.has_changed())
167
+
168
+
169
+ class TestImageBlock(TestImageChooserBlock):
170
+ def test_render(self):
171
+ block = ImageBlock()
172
+ value = {
173
+ "image": self.image.id, # An id is expected
174
+ "alt_text": "Sample alt text",
175
+ "decorative": False,
176
+ }
177
+ html = block.render(block.to_python(value))
178
+ soup = WagtailTestUtils.get_soup(html)
179
+ img_tag = soup.find("img")
180
+
181
+ # check specific attributes
182
+ self.assertEqual(img_tag["alt"], value.get("alt_text"))
183
+ self.assertIn("/media/images/test", img_tag["src"])
184
+
185
+ def test_render_basic(self):
186
+ block = ImageBlock()
187
+ value = {
188
+ "image": self.image.id, # An id is expected
189
+ "alt_text": "Sample alt text",
190
+ "decorative": False,
191
+ }
192
+ html = block.render_basic(block.to_python(value))
193
+ soup = WagtailTestUtils.get_soup(html)
194
+ img_tag = soup.find("img")
195
+
196
+ # check specific attributes
197
+ self.assertEqual(img_tag["alt"], value.get("alt_text"))
198
+ self.assertIn("/media/images/test", img_tag["src"])
199
+
200
+ def test_render_as_decorative(self):
201
+ block = ImageBlock()
202
+ value = {
203
+ "image": self.image.id, # An id is expected
204
+ "alt_text": "Sample alt text",
205
+ "decorative": True,
206
+ }
207
+ html = block.render(block.to_python(value))
208
+ soup = WagtailTestUtils.get_soup(html)
209
+ img_tag = soup.find("img")
210
+
211
+ # check specific attributes
212
+ self.assertEqual(img_tag["alt"], "")
213
+ self.assertIn("/media/images/test", img_tag["src"])
214
+
215
+ def test_no_alt_text(self):
216
+ block = ImageBlock()
217
+ value = {
218
+ "image": self.image.id, # An id is expected
219
+ "alt_text": None, # No alt text passed
220
+ "decorative": False,
221
+ }
222
+
223
+ # Invalid state when no alt text is given, and image not marked as decorative
224
+ # Should raise a StructBlock validation error
225
+ with self.assertRaises(StructBlockValidationError) as context:
226
+ block.clean(block.to_python(value))
227
+
228
+ # Check the error message
229
+ self.assertIn(
230
+ "Please add some alt text for your image or mark it as decorative",
231
+ str(context.exception.block_errors["alt_text"]),
232
+ )
233
+
234
+ def test_to_python_with_int(self):
235
+ block = ImageBlock()
236
+ value = block.to_python(self.image.id)
237
+
238
+ self.assertEqual(value.id, self.image.id)
239
+ self.assertEqual(value.contextual_alt_text, None)
240
+ self.assertFalse(value.decorative)
241
+
242
+ def test_to_python_with_dict(self):
243
+ block = ImageBlock()
244
+ value = {"image": self.image.id, "alt_text": "Sample text", "decorative": False}
245
+ result = block.to_python(value)
246
+
247
+ self.assertEqual(result.id, self.image.id)
248
+ self.assertEqual(result.contextual_alt_text, "Sample text")
249
+ self.assertFalse(result.decorative)
250
+
251
+ def test_get_searchable_content(self):
252
+ block = ImageBlock()
253
+ value = {
254
+ "image": self.image.id, # An id is expected
255
+ "alt_text": "Sample alt text",
256
+ "decorative": False,
257
+ }
258
+ result = block.get_searchable_content(block.to_python(value))
259
+
260
+ # check specific attributes
261
+ self.assertEqual(result, ["Sample alt text"])
262
+
263
+ def test_required_true(self):
264
+ block = ImageBlock()
265
+
266
+ # the inner ImageChooserBlock should appear as required
267
+ image_block_def = JSContext().pack(block)
268
+ image_chooser_block_def = image_block_def["_args"][1][0]
269
+ self.assertTrue(image_chooser_block_def["_args"][2]["required"])
270
+
271
+ value = block.to_python(
272
+ {
273
+ "image": None,
274
+ "alt_text": "",
275
+ "decorative": False,
276
+ }
277
+ )
278
+ with self.assertRaises(StructBlockValidationError) as context:
279
+ block.clean(value)
280
+
281
+ self.assertIn(
282
+ "This field is required",
283
+ str(context.exception.block_errors["image"]),
284
+ )
285
+
286
+ def test_required_false(self):
287
+ block = ImageBlock(required=False)
288
+
289
+ # the inner ImageChooserBlock should appear as non-required
290
+ image_block_def = JSContext().pack(block)
291
+ image_chooser_block_def = image_block_def["_args"][1][0]
292
+ self.assertFalse(image_chooser_block_def["_args"][2]["required"])
293
+
294
+ value = block.to_python(
295
+ {
296
+ "image": None,
297
+ "alt_text": "",
298
+ "decorative": False,
299
+ }
300
+ )
301
+ self.assertIsNone(block.clean(value))
302
+
303
+
304
+ class TestImageBlockComparison(TestCase):
305
+ comparison_class = compare.StreamFieldComparison
306
+
307
+ def setUp(self):
308
+ self.image_1 = Image.objects.create(
309
+ title="Test image 1",
310
+ file=get_test_image_file(),
311
+ )
312
+
313
+ self.image_2 = Image.objects.create(
314
+ title="Test image 2",
315
+ file=get_test_image_file(),
316
+ )
317
+
318
+ self.field = StreamPage._meta.get_field("body")
319
+
320
+ def test_hasnt_changed(self):
321
+ field = StreamPage._meta.get_field("body")
322
+
323
+ page_1 = StreamPage()
324
+ page_1.body = [
325
+ {
326
+ "type": "image_with_alt",
327
+ "value": {
328
+ "image": self.image_1.id,
329
+ "decorative": False,
330
+ "alt_text": "Some alt text",
331
+ },
332
+ "id": "1",
333
+ },
334
+ ]
335
+ page_2 = StreamPage()
336
+ page_2.body = [
337
+ {
338
+ "type": "image_with_alt",
339
+ "value": {
340
+ "image": self.image_1.id,
341
+ "decorative": False,
342
+ "alt_text": "Some alt text",
343
+ },
344
+ "id": "1",
345
+ },
346
+ ]
347
+
348
+ comparison = self.comparison_class(field, page_1, page_2)
349
+
350
+ self.assertTrue(comparison.is_field)
351
+ self.assertFalse(comparison.is_child_relation)
352
+ self.assertEqual(comparison.field_label(), "Body")
353
+ htmldiff = comparison.htmldiff()
354
+ self.assertIsInstance(htmldiff, SafeString)
355
+ self.assertIn('class="comparison__child-object"', htmldiff)
356
+ self.assertIn('class="preview-image"', htmldiff)
357
+ self.assertNotIn("deletion", htmldiff)
358
+ self.assertNotIn("addition", htmldiff)
359
+ self.assertFalse(comparison.has_changed())
360
+
361
+ def test_has_changed(self):
362
+ field = StreamPage._meta.get_field("body")
363
+
364
+ page_1 = StreamPage()
365
+ page_1.body = [
366
+ {
367
+ "type": "image_with_alt",
368
+ "value": {
369
+ "image": self.image_1.id,
370
+ "decorative": False,
371
+ "alt_text": "Some alt text",
372
+ },
373
+ "id": "1",
374
+ },
375
+ ]
376
+ page_2 = StreamPage()
377
+ page_2.body = [
378
+ {
379
+ "type": "image_with_alt",
380
+ "value": {
381
+ "image": self.image_2.id,
382
+ "decorative": False,
383
+ "alt_text": "Some alt text",
384
+ },
385
+ "id": "1",
386
+ },
387
+ ]
388
+
389
+ comparison = self.comparison_class(field, page_1, page_2)
390
+
391
+ self.assertTrue(comparison.is_field)
392
+ self.assertFalse(comparison.is_child_relation)
393
+ self.assertEqual(comparison.field_label(), "Body")
394
+ htmldiff = comparison.htmldiff()
395
+ self.assertIsInstance(htmldiff, SafeString)
396
+ self.assertIn('class="comparison__child-object"', htmldiff)
397
+ self.assertIn('class="preview-image deletion"', htmldiff)
398
+ self.assertIn('class="preview-image addition"', htmldiff)
399
+ self.assertTrue(comparison.has_changed())
400
+
401
+ def test_alt_text_has_changed(self):
402
+ field = StreamPage._meta.get_field("body")
403
+
404
+ page_1 = StreamPage()
405
+ page_1.body = [
406
+ {
407
+ "type": "image_with_alt",
408
+ "value": {
409
+ "image": self.image_1.id,
410
+ "decorative": False,
411
+ "alt_text": "a cat playing with some string",
412
+ },
413
+ "id": "1",
414
+ },
415
+ ]
416
+ page_2 = StreamPage()
417
+ page_2.body = [
418
+ {
419
+ "type": "image_with_alt",
420
+ "value": {
421
+ "image": self.image_1.id,
422
+ "decorative": False,
423
+ "alt_text": "a kitten playing with some string",
424
+ },
425
+ "id": "1",
426
+ },
427
+ ]
428
+
429
+ comparison = self.comparison_class(field, page_1, page_2)
430
+
431
+ self.assertTrue(comparison.is_field)
432
+ self.assertFalse(comparison.is_child_relation)
433
+ self.assertEqual(comparison.field_label(), "Body")
434
+ htmldiff = comparison.htmldiff()
435
+ self.assertIsInstance(htmldiff, SafeString)
436
+ self.assertIn('class="comparison__child-object"', htmldiff)
437
+ self.assertIn(
438
+ '<dd>a <span class="deletion">cat</span><span class="addition">kitten</span> playing with some string</dd>',
439
+ htmldiff,
440
+ )
441
+ self.assertTrue(comparison.has_changed())
@@ -719,6 +719,29 @@ class TestFormatFilter(TestCase):
719
719
  # quality=80 is default for the Willow and PIL libraries
720
720
  save.assert_called_with(f, "WEBP", quality=80, lossless=True)
721
721
 
722
+ def test_heic(self):
723
+ fil = Filter(spec="width-400|format-heic")
724
+ image = Image.objects.create(
725
+ title="Test image",
726
+ file=get_test_image_file(),
727
+ )
728
+ out = fil.run(image, BytesIO())
729
+
730
+ self.assertEqual(out.format_name, "heic")
731
+
732
+ def test_heic_lossless(self):
733
+ fil = Filter(spec="width-400|format-heic-lossless")
734
+ image = Image.objects.create(
735
+ title="Test image",
736
+ file=get_test_image_file(),
737
+ )
738
+
739
+ f = BytesIO()
740
+ with patch("PIL.Image.Image.save") as save:
741
+ fil.run(image, f)
742
+
743
+ save.assert_called_with(f, "HEIF", quality=-1, chroma=444)
744
+
722
745
  def test_invalid(self):
723
746
  fil = Filter(spec="width-400|format-foo")
724
747
  image = Image.objects.create(
@@ -145,6 +145,23 @@ class TestImage(TestCase):
145
145
  )
146
146
  self.assertIsNone(image.get_suggested_focal_point())
147
147
 
148
+ def test_default_with_description(self):
149
+ # Primary default should be description
150
+ image = Image.objects.create(
151
+ title="Test Image",
152
+ description="This is a test description",
153
+ file=get_test_image_file(),
154
+ )
155
+ self.assertEqual(image.default_alt_text, image.description)
156
+
157
+ def test_default_without_description(self):
158
+ # Secondary default should be title
159
+ image = Image.objects.create(
160
+ title="Test Image",
161
+ file=get_test_image_file(),
162
+ )
163
+ self.assertEqual(image.default_alt_text, image.title)
164
+
148
165
 
149
166
  class TestImageQuerySet(TransactionTestCase):
150
167
  fixtures = ["test_empty.json"]
@@ -787,6 +804,9 @@ class TestRenditions(TestCase):
787
804
  prefetched_rendition = fresh_image.get_rendition("width-500")
788
805
  self.assertFalse(hasattr(prefetched_rendition, "_mark"))
789
806
 
807
+ # Check that the image instance is the same as the retrieved rendition
808
+ self.assertIs(new_rendition.image, self.image)
809
+
790
810
  # changing the image file should invalidate the cache
791
811
  self.image.file = get_test_image_file(colour="green")
792
812
  self.image.save()
@@ -1,95 +1,99 @@
1
- from django.db import transaction
2
- from django.test import TestCase, TransactionTestCase, override_settings
3
-
4
- from wagtail.images import get_image_model, signal_handlers
5
- from wagtail.images.tests.utils import get_test_image_file
6
- from wagtail.models import Collection
7
-
8
- from .utils import Image
9
-
10
-
11
- class TestFilesDeletedForDefaultModels(TransactionTestCase):
12
- """
13
- Because we expect file deletion to only happen once a transaction is
14
- successfully committed, we must run these tests using TransactionTestCase
15
- per the following documentation:
16
-
17
- Django's TestCase class wraps each test in a transaction and rolls back that
18
- transaction after each test, in order to provide test isolation. This means
19
- that no transaction is ever actually committed, thus your on_commit()
20
- callbacks will never be run. If you need to test the results of an
21
- on_commit() callback, use a TransactionTestCase instead.
22
- https://docs.djangoproject.com/en/1.10/topics/db/transactions/#use-in-tests
23
- """
24
-
25
- def setUp(self):
26
- # Required to create root collection because the TransactionTestCase
27
- # does not make initial data loaded in migrations available and
28
- # serialized_rollback=True causes other problems in the test suite.
29
- # ref: https://docs.djangoproject.com/en/1.10/topics/testing/overview/#rollback-emulation
30
- Collection.objects.get_or_create(
31
- name="Root",
32
- path="0001",
33
- depth=1,
34
- numchild=0,
35
- )
36
-
37
- def test_image_file_deleted_oncommit(self):
38
- with transaction.atomic():
39
- image = get_image_model().objects.create(
40
- title="Test Image", file=get_test_image_file()
41
- )
42
- filename = image.file.name
43
- self.assertTrue(image.file.storage.exists(filename))
44
- image.delete()
45
- self.assertTrue(image.file.storage.exists(filename))
46
- self.assertFalse(image.file.storage.exists(filename))
47
-
48
- def test_rendition_file_deleted_oncommit(self):
49
- with transaction.atomic():
50
- image = get_image_model().objects.create(
51
- title="Test Image", file=get_test_image_file()
52
- )
53
- rendition = image.get_rendition("original")
54
- filename = rendition.file.name
55
- self.assertTrue(rendition.file.storage.exists(filename))
56
- rendition.delete()
57
- self.assertTrue(rendition.file.storage.exists(filename))
58
- self.assertFalse(rendition.file.storage.exists(filename))
59
-
60
-
61
- @override_settings(WAGTAILIMAGES_IMAGE_MODEL="tests.CustomImage")
62
- class TestFilesDeletedForCustomModels(TestFilesDeletedForDefaultModels):
63
- def setUp(self):
64
- # Required to create root collection because the TransactionTestCase
65
- # does not make initial data loaded in migrations available and
66
- # serialized_rollback=True causes other problems in the test suite.
67
- # ref: https://docs.djangoproject.com/en/1.10/topics/testing/overview/#rollback-emulation
68
- Collection.objects.get_or_create(
69
- name="Root",
70
- path="0001",
71
- depth=1,
72
- numchild=0,
73
- )
74
-
75
- #: Sadly signal receivers only get connected when starting django.
76
- #: We will re-attach them here to mimic the django startup behaviour
77
- #: and get the signals connected to our custom model..
78
- signal_handlers.register_signal_handlers()
79
-
80
- def test_image_model(self):
81
- cls = get_image_model()
82
- self.assertEqual(f"{cls._meta.app_label}.{cls.__name__}", "tests.CustomImage")
83
-
84
-
85
- @override_settings(WAGTAILIMAGES_FEATURE_DETECTION_ENABLED=True)
86
- class TestRawForPreSaveImageFeatureDetection(TestCase):
87
- fixtures = ["test.json"]
88
-
89
- # just to test the file is from a fixture doesn't actually exists.
90
- # raw check in pre_save_image_feature_detection skips on the provided condition of this test
91
- # hence avoiding an error
92
-
93
- def test_image_does_not_exist(self):
94
- bad_image = Image.objects.get(pk=1)
95
- self.assertFalse(bad_image.file.storage.exists(bad_image.file.name))
1
+ from django.db import transaction
2
+ from django.test import TestCase, TransactionTestCase, override_settings
3
+
4
+ from wagtail.images import get_image_model, signal_handlers
5
+ from wagtail.images.tests.utils import get_test_image_file
6
+ from wagtail.models import Collection
7
+
8
+ from .utils import Image
9
+
10
+
11
+ class TestFilesDeletedForDefaultModels(TransactionTestCase):
12
+ """
13
+ Because we expect file deletion to only happen once a transaction is
14
+ successfully committed, we must run these tests using TransactionTestCase
15
+ per the following documentation:
16
+
17
+ Django's TestCase class wraps each test in a transaction and rolls back that
18
+ transaction after each test, in order to provide test isolation. This means
19
+ that no transaction is ever actually committed, thus your on_commit()
20
+ callbacks will never be run. If you need to test the results of an
21
+ on_commit() callback, use a TransactionTestCase instead.
22
+ https://docs.djangoproject.com/en/1.10/topics/db/transactions/#use-in-tests
23
+ """
24
+
25
+ def setUp(self):
26
+ # Required to create root collection because the TransactionTestCase
27
+ # does not make initial data loaded in migrations available and
28
+ # serialized_rollback=True causes other problems in the test suite.
29
+ # ref: https://docs.djangoproject.com/en/1.10/topics/testing/overview/#rollback-emulation
30
+ Collection.objects.get_or_create(
31
+ name="Root",
32
+ path="0001",
33
+ depth=1,
34
+ numchild=0,
35
+ )
36
+
37
+ def test_image_file_deleted_oncommit(self):
38
+ with transaction.atomic():
39
+ image = get_image_model().objects.create(
40
+ title="Test Image",
41
+ description="A test description",
42
+ file=get_test_image_file(),
43
+ )
44
+ filename = image.file.name
45
+ self.assertTrue(image.file.storage.exists(filename))
46
+ image.delete()
47
+ self.assertTrue(image.file.storage.exists(filename))
48
+ self.assertFalse(image.file.storage.exists(filename))
49
+
50
+ def test_rendition_file_deleted_oncommit(self):
51
+ with transaction.atomic():
52
+ image = get_image_model().objects.create(
53
+ title="Test Image",
54
+ description="A test description",
55
+ file=get_test_image_file(),
56
+ )
57
+ rendition = image.get_rendition("original")
58
+ filename = rendition.file.name
59
+ self.assertTrue(rendition.file.storage.exists(filename))
60
+ rendition.delete()
61
+ self.assertTrue(rendition.file.storage.exists(filename))
62
+ self.assertFalse(rendition.file.storage.exists(filename))
63
+
64
+
65
+ @override_settings(WAGTAILIMAGES_IMAGE_MODEL="tests.CustomImage")
66
+ class TestFilesDeletedForCustomModels(TestFilesDeletedForDefaultModels):
67
+ def setUp(self):
68
+ # Required to create root collection because the TransactionTestCase
69
+ # does not make initial data loaded in migrations available and
70
+ # serialized_rollback=True causes other problems in the test suite.
71
+ # ref: https://docs.djangoproject.com/en/1.10/topics/testing/overview/#rollback-emulation
72
+ Collection.objects.get_or_create(
73
+ name="Root",
74
+ path="0001",
75
+ depth=1,
76
+ numchild=0,
77
+ )
78
+
79
+ #: Sadly signal receivers only get connected when starting django.
80
+ #: We will re-attach them here to mimic the django startup behaviour
81
+ #: and get the signals connected to our custom model..
82
+ signal_handlers.register_signal_handlers()
83
+
84
+ def test_image_model(self):
85
+ cls = get_image_model()
86
+ self.assertEqual(f"{cls._meta.app_label}.{cls.__name__}", "tests.CustomImage")
87
+
88
+
89
+ @override_settings(WAGTAILIMAGES_FEATURE_DETECTION_ENABLED=True)
90
+ class TestRawForPreSaveImageFeatureDetection(TestCase):
91
+ fixtures = ["test.json"]
92
+
93
+ # just to test the file is from a fixture doesn't actually exists.
94
+ # raw check in pre_save_image_feature_detection skips on the provided condition of this test
95
+ # hence avoiding an error
96
+
97
+ def test_image_does_not_exist(self):
98
+ bad_image = Image.objects.get(pk=1)
99
+ self.assertFalse(bad_image.file.storage.exists(bad_image.file.name))
@@ -126,9 +126,9 @@ class TestImagesSummary(WagtailTestUtils, TestCase):
126
126
 
127
127
  def test_user_sees_proper_image_count(self):
128
128
  cases = (
129
- (self.superuser, "<span>3</span> Images"),
130
- (self.bird_adder, "<span>2</span> Images"),
131
- (self.bird_chooser, "<span>2</span> Images"),
129
+ (self.superuser, "3 Images"),
130
+ (self.bird_adder, "2 Images"),
131
+ (self.bird_chooser, "2 Images"),
132
132
  )
133
133
  for user, content in cases:
134
134
  with self.subTest(user=user):