wagtail 6.2.2__py3-none-any.whl → 6.3rc2__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 +12 -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.3rc2.dist-info}/METADATA +6 -6
  374. {wagtail-6.2.2.dist-info → wagtail-6.3rc2.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.3rc2.dist-info}/LICENSE +0 -0
  384. {wagtail-6.2.2.dist-info → wagtail-6.3rc2.dist-info}/WHEEL +0 -0
  385. {wagtail-6.2.2.dist-info → wagtail-6.3rc2.dist-info}/entry_points.txt +0 -0
  386. {wagtail-6.2.2.dist-info → wagtail-6.3rc2.dist-info}/top_level.txt +0 -0
wagtail/fields.py CHANGED
@@ -3,7 +3,6 @@ import json
3
3
  from django.core.serializers.json import DjangoJSONEncoder
4
4
  from django.core.validators import MaxLengthValidator
5
5
  from django.db import models
6
- from django.db.models.fields.json import KeyTransform
7
6
  from django.utils.encoding import force_str
8
7
  from django.utils.functional import cached_property
9
8
 
@@ -185,6 +184,7 @@ class StreamField(models.Field):
185
184
  return result
186
185
 
187
186
  def get_prep_value(self, value):
187
+ value = super().get_prep_value(value)
188
188
  if (
189
189
  isinstance(value, StreamValue)
190
190
  and not (value)
@@ -197,36 +197,37 @@ class StreamField(models.Field):
197
197
  return value.raw_text
198
198
  elif isinstance(value, StreamValue):
199
199
  # StreamValue instances must be prepared first.
200
- return json.dumps(
201
- self.stream_block.get_prep_value(value), cls=DjangoJSONEncoder
202
- )
200
+ return self.stream_block.get_prep_value(value)
203
201
  else:
204
- # When querying with JSONField features, the rhs might not be a StreamValue.
205
- # Note: when Django 4.2 is the minimum supported version, this can be removed
206
- # as the serialisation is handled in get_db_prep_value instead.
207
- return self.json_field.get_prep_value(value)
202
+ # If the value is not a StreamValue, it's likely the field is being
203
+ # used in a non-Wagtail context, e.g. in queries with JSONField features.
204
+ return super().get_prep_value(value)
208
205
 
209
206
  def get_db_prep_value(self, value, connection, prepared=False):
210
- if not isinstance(value, StreamValue):
211
- # When querying with JSONField features, the rhs might not be a StreamValue.
212
- # As of Django 4.2, JSONField value serialisation is handled in
213
- # get_db_prep_value instead of get_prep_value.
214
- return self.json_field.get_db_prep_value(value, connection, prepared)
215
- return super().get_db_prep_value(value, connection, prepared)
207
+ # Use JSONField's get_db_prep_value method to handle the serialization,
208
+ # which may differ between database backends. However, use our own
209
+ # get_prep_value method to ensure that StreamValue instances are prepared
210
+ # before being passed to JSONField.
211
+ if not prepared:
212
+ value = self.get_prep_value(value)
213
+ return self.json_field.get_db_prep_value(
214
+ value, connection=connection, prepared=True
215
+ )
216
216
 
217
217
  def from_db_value(self, value, expression, connection):
218
- if isinstance(expression, KeyTransform):
219
- # This could happen when using JSONField key transforms,
220
- # e.g. Page.object.values('body__0').
221
- try:
222
- # We might be able to properly resolve to the appropriate StreamValue
223
- # based on `expression` and `self.stream_block`, but it might be too
224
- # complicated to do so. For now, just deserialise the value.
225
- return json.loads(value)
226
- except ValueError:
227
- # Just in case the extracted value is not valid JSON.
228
- return value
229
-
218
+ # Historically, StreamField's deserialization used to be handled by
219
+ # to_python, which in turn handled by BaseStreamBlock.to_python. This was
220
+ # always the case even before and after the use of the JSON data type.
221
+
222
+ # However, now that we can be confident all StreamField data has been
223
+ # migrated to use JSON in the database, we can reuse any special handling
224
+ # that JSONField.from_db_value provides, e.g. for handling KeyTransforms
225
+ # on SQLite.
226
+
227
+ # This means we are passing a deserialized value to StreamBlock.to_python,
228
+ # which is a change from the previous behaviour. However, this is fine
229
+ # because to_python can handle both serialized and deserialized values.
230
+ value = self.json_field.from_db_value(value, expression, connection)
230
231
  return self.to_python(value)
231
232
 
232
233
  def formfield(self, **kwargs):
@@ -239,8 +240,23 @@ class StreamField(models.Field):
239
240
  return super().formfield(**defaults)
240
241
 
241
242
  def value_to_string(self, obj):
243
+ # This method is used for serialization using django.core.serializers,
244
+ # which is used by dumpdata and loaddata for serializing model objects.
245
+ # Unlike other fields, JSONField only uses value_from_object without
246
+ # doing the actual serialization, so that it doesn't end up being
247
+ # double-serialized when the model object is serialized.
248
+
249
+ # Unfortunately, this is also used by django-modelcluster, which is used
250
+ # to serialize model objects to be stored in revisions. When we migrated
251
+ # StreamField to use the JSON data type, we did not change this method's
252
+ # behaviour, i.e. it still returns a JSON-shaped string, to ensure that
253
+ # revisions are still saved in the same format as before – even if it
254
+ # means StreamField inside the revision data becomes double-serialized.
255
+
256
+ # Now that we change get_prep_value to not do the serialization in favor
257
+ # of get_db_prep_value, we need to add the serialization here too.
242
258
  value = self.value_from_object(obj)
243
- return self.get_prep_value(value)
259
+ return json.dumps(self.get_prep_value(value), cls=self.json_field.encoder)
244
260
 
245
261
  def get_searchable_content(self, value):
246
262
  return self.stream_block.get_searchable_content(value)
@@ -6,7 +6,7 @@ app_name = "wagtailimages"
6
6
  urlpatterns = [
7
7
  path("", images.IndexView.as_view(), name="index"),
8
8
  path("results/", images.IndexView.as_view(results_only=True), name="index_results"),
9
- path("<int:image_id>/", images.edit, name="edit"),
9
+ path("<int:image_id>/", images.EditView.as_view(), name="edit"),
10
10
  path("<int:image_id>/delete/", images.DeleteView.as_view(), name="delete"),
11
11
  path(
12
12
  "<int:image_id>/generate_url/",
@@ -19,7 +19,7 @@ urlpatterns = [
19
19
  name="generate_url",
20
20
  ),
21
21
  path("<int:image_id>/preview/<str:filter_spec>/", images.preview, name="preview"),
22
- path("add/", images.add, name="add"),
22
+ path("add/", images.CreateView.as_view(), name="add"),
23
23
  path("usage/<int:image_id>/", images.UsageView.as_view(), name="image_usage"),
24
24
  path("multiple/add/", multiple.AddView.as_view(), name="add_multiple"),
25
25
  path("multiple/<int:image_id>/", multiple.EditView.as_view(), name="edit_multiple"),
wagtail/images/blocks.py CHANGED
@@ -1,8 +1,14 @@
1
+ from django import forms
2
+ from django.core.exceptions import ValidationError
1
3
  from django.template.loader import render_to_string
2
4
  from django.utils.functional import cached_property
5
+ from django.utils.translation import gettext_lazy as _
3
6
 
4
- from wagtail.admin.compare import BlockComparison
5
- from wagtail.blocks import ChooserBlock
7
+ from wagtail.admin.compare import BlockComparison, StructBlockComparison
8
+ from wagtail.blocks import BooleanBlock, CharBlock, ChooserBlock, StructBlock
9
+ from wagtail.blocks.struct_block import StructBlockAdapter, StructBlockValidationError
10
+ from wagtail.images.models import AbstractImage
11
+ from wagtail.telepath import register
6
12
 
7
13
  from .shortcuts import get_rendition_or_not_found
8
14
 
@@ -51,3 +57,225 @@ class ImageChooserBlockComparison(BlockComparison):
51
57
  "image_b": self.val_b,
52
58
  },
53
59
  )
60
+
61
+
62
+ class ImageBlock(StructBlock):
63
+ """
64
+ An usage of ImageChooserBlock with support for alt text.
65
+ For backward compatibility, this block overrides necessary methods to change
66
+ the StructValue to be an Image model instance, making it compatible in
67
+ places where ImageChooserBlock was used.
68
+ """
69
+
70
+ image = ImageChooserBlock(required=True)
71
+ decorative = BooleanBlock(
72
+ default=False, required=False, label=_("Image is decorative")
73
+ )
74
+ alt_text = CharBlock(required=False, label=_("Alt text"))
75
+
76
+ def __init__(self, required=True, **kwargs):
77
+ super().__init__(
78
+ [
79
+ ("image", ImageChooserBlock(required=required)),
80
+ (
81
+ "decorative",
82
+ BooleanBlock(
83
+ default=False, required=False, label=_("Image is decorative")
84
+ ),
85
+ ),
86
+ ("alt_text", CharBlock(required=False, label=_("Alt text"))),
87
+ ],
88
+ **kwargs,
89
+ )
90
+
91
+ def deconstruct(self):
92
+ """
93
+ For typical StructBlock subclasses, it makes sense for the deconstructed block object to be a basic StructBlock
94
+ with the child blocks passed to the constructor (because this is largely functionally identical to the
95
+ subclass, and avoids embedding a reference to a potentially-volatile custom class in migrations).
96
+
97
+ This is not the case for ImageBlock, as it overrides enough of StructBlock's behaviour that a basic StructBlock
98
+ is not a suitable substitute - and also has an incompatible constructor signature (as we don't want to support
99
+ passing child blocks to it).
100
+
101
+ Therefore, we opt out of the standard StructBlock deconstruction behaviour here, and always
102
+ deconstruct an ImageBlock as an ImageBlock.
103
+ """
104
+ return ("wagtail.images.blocks.ImageBlock", [], self._constructor_kwargs)
105
+
106
+ def deconstruct_with_lookup(self, lookup):
107
+ return self.deconstruct()
108
+
109
+ @classmethod
110
+ def construct_from_lookup(cls, lookup, *args, **kwargs):
111
+ return cls(**kwargs)
112
+
113
+ def get_searchable_content(self, value):
114
+ if not self.search_index or not value:
115
+ return []
116
+
117
+ return self.child_blocks["alt_text"].get_searchable_content(
118
+ value.contextual_alt_text
119
+ )
120
+
121
+ def _struct_value_to_image(self, struct_value):
122
+ image = struct_value.get("image")
123
+ decorative = struct_value.get("decorative")
124
+ if image:
125
+ # If the image is decorative, set alt_text to an empty string
126
+ image.contextual_alt_text = (
127
+ "" if decorative else struct_value.get("alt_text")
128
+ )
129
+ image.decorative = decorative
130
+ return image
131
+
132
+ def _image_to_struct_value(self, image):
133
+ return {
134
+ "image": image,
135
+ "alt_text": image and image.contextual_alt_text,
136
+ "decorative": image and image.decorative,
137
+ }
138
+
139
+ def to_python(self, value):
140
+ # For backward compatibility with ImageChooserBlock
141
+ if isinstance(value, int):
142
+ image = self.child_blocks["image"].to_python(value)
143
+ struct_value = {"image": image, "decorative": False, "alt_text": None}
144
+ else:
145
+ struct_value = super().to_python(value)
146
+ return self._struct_value_to_image(struct_value)
147
+
148
+ def bulk_to_python(self, values):
149
+ values = list(values)
150
+
151
+ if not values:
152
+ return []
153
+
154
+ if isinstance(values[0], int):
155
+ # `values` is a list of image IDs (as we might encounter if an ImageChooserBlock has been
156
+ # changed to an ImageBlock with no data migration)
157
+ image_values = self.child_blocks["image"].bulk_to_python(values)
158
+
159
+ struct_values = [
160
+ {
161
+ "image": image,
162
+ "decorative": False,
163
+ "alt_text": None,
164
+ }
165
+ for image in image_values
166
+ ]
167
+
168
+ else:
169
+ # assume `values` is a list of dicts containing `image`, `decorative` and `alt_text` keys
170
+ # to be handled by the StructBlock superclass
171
+ struct_values = super().bulk_to_python(values)
172
+
173
+ return [
174
+ self._struct_value_to_image(struct_value) for struct_value in struct_values
175
+ ]
176
+
177
+ def value_from_datadict(self, data, files, prefix):
178
+ struct_value = super().value_from_datadict(data, files, prefix)
179
+ return self._struct_value_to_image(struct_value)
180
+
181
+ def clean(self, value):
182
+ try:
183
+ self.child_blocks["image"].clean(value)
184
+ except ValidationError as e:
185
+ raise StructBlockValidationError(
186
+ block_errors={"image": e},
187
+ )
188
+
189
+ if value and not value.contextual_alt_text and not value.decorative:
190
+ raise StructBlockValidationError(
191
+ block_errors={
192
+ "alt_text": ValidationError(
193
+ _(
194
+ "Please add some alt text for your image or mark it as decorative"
195
+ )
196
+ )
197
+ }
198
+ )
199
+
200
+ return value
201
+
202
+ def normalize(self, value):
203
+ if value is None or isinstance(value, AbstractImage):
204
+ return value
205
+ else:
206
+ struct_value = super().normalize(value)
207
+ return self._struct_value_to_image(struct_value)
208
+
209
+ def get_form_context(self, value, prefix="", errors=None):
210
+ dict_value = {
211
+ "image": value,
212
+ "alt_text": value and value.contextual_alt_text,
213
+ "decorative": value and value.decorative,
214
+ }
215
+ context = super().get_form_context(dict_value, prefix=prefix, errors=errors)
216
+ context["suggested_alt_text"] = value
217
+ return context
218
+
219
+ def get_form_state(self, value):
220
+ return {
221
+ "image": self.child_blocks["image"].get_form_state(value),
222
+ "alt_text": value and value.contextual_alt_text,
223
+ "decorative": value and value.decorative,
224
+ }
225
+
226
+ def get_prep_value(self, value):
227
+ return {
228
+ "image": self.child_blocks["image"].get_prep_value(value),
229
+ "alt_text": value and value.contextual_alt_text,
230
+ "decorative": value and value.decorative,
231
+ }
232
+
233
+ def extract_references(self, value):
234
+ return self.child_blocks["image"].extract_references(value)
235
+
236
+ def get_comparison_class(self):
237
+ return ImageBlockComparison
238
+
239
+ def get_api_representation(self, value, context=None):
240
+ return super().get_api_representation(
241
+ self._image_to_struct_value(value), context=context
242
+ )
243
+
244
+ def render_basic(self, value, context=None):
245
+ return self.child_blocks["image"].render_basic(value, context=context)
246
+
247
+ class Meta:
248
+ icon = "image"
249
+ template = "wagtailimages/widgets/image.html"
250
+
251
+
252
+ class ImageBlockAdapter(StructBlockAdapter):
253
+ js_constructor = "wagtail.images.blocks.ImageBlock"
254
+
255
+ @cached_property
256
+ def media(self):
257
+ structblock_media = super().media
258
+ return forms.Media(
259
+ js=structblock_media._js + ["wagtailimages/js/image-block.js"],
260
+ css=structblock_media._css,
261
+ )
262
+
263
+
264
+ register(ImageBlockAdapter(), ImageBlock)
265
+
266
+
267
+ class ImageBlockComparison(StructBlockComparison):
268
+ def __init__(self, block, exists_a, exists_b, val_a, val_b):
269
+ super().__init__(
270
+ block,
271
+ exists_a,
272
+ exists_b,
273
+ block._image_to_struct_value(val_a),
274
+ block._image_to_struct_value(val_b),
275
+ )
276
+
277
+ def htmlvalue(self, val):
278
+ if isinstance(val, AbstractImage):
279
+ return super().htmlvalue(self.block._image_to_struct_value(val))
280
+ else:
281
+ return super().htmlvalue(val)
wagtail/images/fields.py CHANGED
@@ -6,6 +6,7 @@ from django.conf import settings
6
6
  from django.core.exceptions import ValidationError
7
7
  from django.core.validators import FileExtensionValidator
8
8
  from django.forms.fields import FileField, ImageField
9
+ from django.forms.widgets import FileInput
9
10
  from django.template.defaultfilters import filesizeformat
10
11
  from django.utils.translation import gettext_lazy as _
11
12
 
@@ -32,6 +33,8 @@ class WagtailImageField(ImageField):
32
33
  default_validators = [ImageFileExtensionValidator]
33
34
 
34
35
  def __init__(self, *args, **kwargs):
36
+ self.allowed_image_extensions = get_allowed_image_extensions()
37
+
35
38
  super().__init__(*args, **kwargs)
36
39
 
37
40
  # Get max upload size from settings
@@ -43,8 +46,6 @@ class WagtailImageField(ImageField):
43
46
  )
44
47
  self.max_upload_size_text = filesizeformat(self.max_upload_size)
45
48
 
46
- self.allowed_image_extensions = get_allowed_image_extensions()
47
-
48
49
  self.supported_formats_text = ", ".join(self.allowed_image_extensions).upper()
49
50
 
50
51
  # Help text
@@ -163,7 +164,7 @@ class WagtailImageField(ImageField):
163
164
  # Annotate the python representation of the FileField with the image
164
165
  # property so subclasses can reuse it for their own validation
165
166
  f.image = willow.Image.open(file)
166
- f.content_type = image_format_name_to_content_type(f.image.format_name)
167
+ f.content_type = f.image.mime_type
167
168
 
168
169
  except Exception as exc: # noqa: BLE001
169
170
  # Willow doesn't recognize it as an image.
@@ -182,30 +183,17 @@ class WagtailImageField(ImageField):
182
183
 
183
184
  return f
184
185
 
186
+ def widget_attrs(self, widget):
187
+ attrs = super().widget_attrs(widget)
188
+
189
+ if (
190
+ isinstance(widget, FileInput)
191
+ and "accept" not in widget.attrs
192
+ and attrs.get("accept") == "image/*"
193
+ and "heic" in self.allowed_image_extensions
194
+ ):
195
+ # File upload dialogs (at least on Chrome / Mac) don't count heic as part of image/*, as it's not a
196
+ # web-safe format, so add it explicitly
197
+ attrs["accept"] = "image/*, image/heic"
185
198
 
186
- def image_format_name_to_content_type(image_format_name):
187
- """
188
- Convert a Willow image format name to a content type.
189
- TODO: Replace once https://github.com/wagtail/Willow/pull/102 and
190
- a new Willow release is out
191
- """
192
- if image_format_name == "svg":
193
- return "image/svg+xml"
194
- elif image_format_name == "jpeg":
195
- return "image/jpeg"
196
- elif image_format_name == "png":
197
- return "image/png"
198
- elif image_format_name == "gif":
199
- return "image/gif"
200
- elif image_format_name == "bmp":
201
- return "image/bmp"
202
- elif image_format_name == "tiff":
203
- return "image/tiff"
204
- elif image_format_name == "webp":
205
- return "image/webp"
206
- elif image_format_name == "avif":
207
- return "image/avif"
208
- elif image_format_name == "ico":
209
- return "image/x-icon"
210
- else:
211
- raise ValueError("Unknown image format name")
199
+ return attrs
@@ -409,7 +409,7 @@ class WebPQualityOperation(FilterOperation):
409
409
 
410
410
 
411
411
  class FormatOperation(FilterOperation):
412
- supported_formats = ["jpeg", "png", "gif", "webp", "avif", "ico"]
412
+ supported_formats = ["jpeg", "png", "gif", "webp", "avif", "ico", "heic"]
413
413
 
414
414
  def construct(self, format, *options):
415
415
  self.format = format
@@ -6,13 +6,14 @@
6
6
  # Adam Hughes <adamhughes31@gmail.com>, 2020
7
7
  # Philip Crisp, 2022
8
8
  # Philip Crisp, 2022
9
+ # Philip Lindsay-Crisp, 2024
9
10
  msgid ""
10
11
  msgstr ""
11
12
  "Project-Id-Version: Wagtail\n"
12
13
  "Report-Msgid-Bugs-To: \n"
13
14
  "POT-Creation-Date: 2024-08-07 10:06+0100\n"
14
15
  "PO-Revision-Date: 2014-02-20 21:05+0000\n"
15
- "Last-Translator: Philip Crisp, 2022\n"
16
+ "Last-Translator: Philip Lindsay-Crisp, 2024\n"
16
17
  "Language-Team: Welsh (http://app.transifex.com/torchbox/wagtail/language/"
17
18
  "cy/)\n"
18
19
  "MIME-Version: 1.0\n"
@@ -162,6 +163,14 @@ msgstr "delwedd"
162
163
  msgid "images"
163
164
  msgstr "delweddau"
164
165
 
166
+ #, python-format
167
+ msgid "Add tags to 1 image"
168
+ msgid_plural "Add tags to %(counter)s images"
169
+ msgstr[0] "Ychwanegu 1 tag i l;lun"
170
+ msgstr[1] "Ychwanegu tagiau i %(counter)s llun"
171
+ msgstr[2] "Ychwanegu tagiau i %(counter)s llun"
172
+ msgstr[3] "Ychwanegu tagiau i %(counter)s llun"
173
+
165
174
  msgid "Add tags to images"
166
175
  msgstr "Ychwanegu tagiau i lluniau"
167
176
 
@@ -185,6 +194,14 @@ msgstr "Ia, ychwanegu"
185
194
  msgid "No, don't add"
186
195
  msgstr "Na, paid a ychwanegu"
187
196
 
197
+ #, python-format
198
+ msgid "Add 1 image to new collection"
199
+ msgid_plural "Add %(counter)s images to new collection"
200
+ msgstr[0] "Ychwanegu 1 casgliad newydd"
201
+ msgstr[1] "Ychwanegu %(counter)s defnyddio casgliad newydd"
202
+ msgstr[2] "Ychwanegu %(counter)s defnyddio casgliad newydd"
203
+ msgstr[3] "Ychwanegu %(counter)s defnyddio casgliad newydd"
204
+
188
205
  msgid "Add images to collection"
189
206
  msgstr "Ychwanegu lluniau i'r casgliad"
190
207
 
@@ -254,18 +271,72 @@ msgstr "Na, peidiwch a ddileu"
254
271
  msgid "Popular tags"
255
272
  msgstr "Tagiau poblogaidd"
256
273
 
274
+ msgid ""
275
+ "Upload successful. However, your new image seems to be a duplicate of an "
276
+ "existing image. You may delete it if it wasn't required."
277
+ msgstr ""
278
+ "Llwytho i fyny yn llwyddiannus. Fodd bynnag, mae'n ymddangos bod eich llun "
279
+ "newydd yn ddyblyg o llun sy'n bodoli eisoes. Gallwch ei ddileu os nad oedd "
280
+ "ei angen."
281
+
282
+ msgid "Existing"
283
+ msgstr "Presennol"
284
+
257
285
  msgid "New"
258
286
  msgstr "Newydd"
259
287
 
288
+ msgid "Use new image"
289
+ msgstr "Defyddio llun newydd."
290
+
291
+ msgid "Use existing and delete new"
292
+ msgstr "Defnyddio presennol a dileu'r newydd"
293
+
260
294
  msgid "Latest images"
261
295
  msgstr "Lluniau diweddaraf"
262
296
 
297
+ #, python-format
298
+ msgid "Select %(title)s"
299
+ msgstr "Dewis %(title)s"
300
+
301
+ msgid "You haven't uploaded any images in this collection."
302
+ msgstr "Nid ydych wedi uwchlwytho unrhyw ddelweddau yn y casgliad hwn."
303
+
304
+ msgid "You haven't uploaded any images."
305
+ msgstr "Nid ydych wedi uwchlwytho unrhyw ddelweddau."
306
+
307
+ msgid ""
308
+ "Why not <a class=\"upload-one-now\" href=\"#tab-upload\" data-tab-"
309
+ "trigger>upload one now</a>?"
310
+ msgstr ""
311
+ "Beth am <a class=\"upload-one-now\" href=\"#tab-upload\" data-tab-"
312
+ "trigger>uwchlwytho un rwan</a>?"
313
+
263
314
  msgid "Choose a format"
264
315
  msgstr "Dewisiwch fformat"
265
316
 
266
317
  msgid "Insert image"
267
318
  msgstr "Mewnosod llun"
268
319
 
320
+ #, python-format
321
+ msgid ""
322
+ "<span>%(total)s</span> Image <span class=\"w-sr-only\">created in "
323
+ "%(site_name)s</span>"
324
+ msgid_plural ""
325
+ "<span>%(total)s</span> Images <span class=\"w-sr-only\">created in "
326
+ "%(site_name)s</span>"
327
+ msgstr[0] ""
328
+ "<span>%(total)s</span> llun <span class=\"w-sr-only\">wedi'i greu i "
329
+ "%(site_name)s</span>"
330
+ msgstr[1] ""
331
+ "<span>%(total)s</span> lluniau <span class=\"w-sr-only\">wedi'i greu "
332
+ "%(site_name)s</span>"
333
+ msgstr[2] ""
334
+ "<span>%(total)s</span> lluniau <span class=\"w-sr-only\">wedi'i greu "
335
+ "%(site_name)s</span>"
336
+ msgstr[3] ""
337
+ "<span>%(total)s</span> lluniau <span class=\"w-sr-only\">wedi'i greu i "
338
+ "%(site_name)s</span>"
339
+
269
340
  msgid "Change image file:"
270
341
  msgstr "Newid ffeil llun:"
271
342
 
@@ -278,6 +349,12 @@ msgstr "Ychwanegu llun"
278
349
  msgid "Save"
279
350
  msgstr "Cadw"
280
351
 
352
+ msgid "Keep new image"
353
+ msgstr "Cadw llun newydd"
354
+
355
+ msgid "Delete new image"
356
+ msgstr "Dileu llun newydd"
357
+
281
358
  #, python-format
282
359
  msgid "Editing image %(title)s"
283
360
  msgstr "Golygu llun %(title)s"
@@ -288,6 +365,9 @@ msgstr "Golygu"
288
365
  msgid "Delete image"
289
366
  msgstr "Dileu delwedd"
290
367
 
368
+ msgid "Image focal point"
369
+ msgstr "Ffocws llun"
370
+
291
371
  msgid "URL Generator"
292
372
  msgstr "Cynhyrchydd URL"
293
373
 
@@ -322,6 +402,9 @@ msgstr "Ffeil heb ei chanfod"
322
402
  msgid "Usage"
323
403
  msgstr "Defnydd"
324
404
 
405
+ msgid "Sort by"
406
+ msgstr "Trefnu un ôl"
407
+
325
408
  msgid "Select all images in listing"
326
409
  msgstr "Dewiswch yr holl lluniau yn y rhestr"
327
410
 
@@ -352,6 +435,18 @@ msgstr "Rhagolwg"
352
435
  msgid "URL"
353
436
  msgstr "URL"
354
437
 
438
+ msgid "Image URL"
439
+ msgstr "URL Llun"
440
+
441
+ msgid "Copy URL"
442
+ msgstr "URL Copi"
443
+
444
+ msgid "Copied to clipboard"
445
+ msgstr "Wedi'i gopïo i'r clipfwrdd"
446
+
447
+ msgid "Copying to clipboard failed"
448
+ msgstr "Methodd copïo i'r clipfwrdd"
449
+
355
450
  msgid "Add multiple images"
356
451
  msgstr "Ychwanegu lluniau lluosog"
357
452
 
@@ -376,6 +471,14 @@ msgstr ""
376
471
  "priodol, os oes angen. Gallwch hefyd ddileu'r llun yn gyfan gwbl os nad oedd "
377
472
  "angen yr uwchlwythiad."
378
473
 
474
+ msgid ""
475
+ "Upload successful. However, your new image seems to be a duplicate of this "
476
+ "existing image. You may delete it if it wasn't required."
477
+ msgstr ""
478
+ "Llwytho i fyny yn llwyddiannus. Fodd bynnag, mae'n ymddangos bod eich llun "
479
+ "newydd yn ddyblyg o'r llun bresennol hon. Gallwch ei ddileu os nad oedd ei "
480
+ "angen."
481
+
379
482
  msgid "Sorry, upload failed."
380
483
  msgstr "Mae'n ddrwg gennym, ni fu modd uwchlwytho."
381
484
 
@@ -397,6 +500,9 @@ msgstr "Caniatadau llun"
397
500
  msgid "Add an image permission"
398
501
  msgstr "Ychwanegu caniatad llun"
399
502
 
503
+ msgid "Tags"
504
+ msgstr "Tagiau"
505
+
400
506
  msgid "Tag"
401
507
  msgstr "Tag"
402
508
 
@@ -452,9 +558,30 @@ msgstr "Wrthi'n uwchlwytho…"
452
558
  msgid "Choose an image"
453
559
  msgstr "Dewis delwedd"
454
560
 
561
+ msgid "Choose another image"
562
+ msgstr "Dewisiwch llun arall"
563
+
455
564
  msgid "Edit this image"
456
565
  msgstr "Golygu'r llun hon"
457
566
 
567
+ msgid "Newest"
568
+ msgstr "Ieuengaf"
569
+
570
+ msgid "Oldest"
571
+ msgstr "Hynaf"
572
+
573
+ msgid "Title: (A -> Z)"
574
+ msgstr "Teitl: (A -> Z)"
575
+
576
+ msgid "Title: (Z -> A)"
577
+ msgstr "Teitl: (Z -> A)"
578
+
579
+ msgid "File size: (low to high)"
580
+ msgstr "Maint ffeil: (isel i uchel)"
581
+
582
+ msgid "File size: (high to low)"
583
+ msgstr "Maint ffeil: (uchel i isel)"
584
+
458
585
  msgid "Images"
459
586
  msgstr "Delweddau"
460
587