wagtail 6.3.2__py3-none-any.whl → 6.4rc1__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 (296) hide show
  1. wagtail/__init__.py +1 -1
  2. wagtail/actions/publish_revision.py +4 -5
  3. wagtail/admin/auth.py +0 -2
  4. wagtail/admin/checks.py +1 -1
  5. wagtail/admin/filters.py +3 -1
  6. wagtail/admin/forms/account.py +21 -11
  7. wagtail/admin/forms/collections.py +2 -9
  8. wagtail/admin/forms/formsets.py +32 -0
  9. wagtail/admin/forms/pages.py +5 -1
  10. wagtail/admin/forms/workflows.py +2 -13
  11. wagtail/admin/locale/ar/LC_MESSAGES/django.mo +0 -0
  12. wagtail/admin/locale/ar/LC_MESSAGES/django.po +68 -1
  13. wagtail/admin/locale/ar/LC_MESSAGES/djangojs.mo +0 -0
  14. wagtail/admin/locale/ar/LC_MESSAGES/djangojs.po +5 -1
  15. wagtail/admin/locale/en/LC_MESSAGES/django.po +312 -356
  16. wagtail/admin/locale/en/LC_MESSAGES/djangojs.po +21 -16
  17. wagtail/admin/menu.py +0 -13
  18. wagtail/admin/panels/base.py +2 -2
  19. wagtail/admin/panels/group.py +4 -1
  20. wagtail/admin/panels/inline_panel.py +5 -2
  21. wagtail/admin/panels/model_utils.py +36 -0
  22. wagtail/admin/panels/page_utils.py +2 -40
  23. wagtail/admin/panels/signal_handlers.py +0 -2
  24. wagtail/admin/static/wagtailadmin/css/core.css +1 -1
  25. wagtail/admin/static/wagtailadmin/css/panels/draftail.css +1 -1
  26. wagtail/admin/static/wagtailadmin/css/panels/streamfield.css +1 -1
  27. wagtail/admin/static/wagtailadmin/js/comments.js +1 -1
  28. wagtail/admin/static/wagtailadmin/js/core.js +1 -1
  29. wagtail/admin/static/wagtailadmin/js/core.js.LICENSE.txt +1 -8
  30. wagtail/admin/static/wagtailadmin/js/draftail.js +1 -1
  31. wagtail/admin/static/wagtailadmin/js/modal-workflow.js +1 -1
  32. wagtail/admin/static/wagtailadmin/js/page-chooser-modal.js +1 -1
  33. wagtail/admin/static/wagtailadmin/js/privacy-switch.js +1 -1
  34. wagtail/admin/static/wagtailadmin/js/sidebar.js +1 -1
  35. wagtail/admin/static/wagtailadmin/js/telepath/blocks.js +1 -1
  36. wagtail/admin/static/wagtailadmin/js/userbar.js +1 -1
  37. wagtail/admin/static/wagtailadmin/js/userbar.js.LICENSE.txt +1 -1
  38. wagtail/admin/static/wagtailadmin/js/vendor.js +1 -1
  39. wagtail/admin/static/wagtailadmin/js/vendor.js.LICENSE.txt +7 -0
  40. wagtail/admin/templates/wagtailadmin/404.html +4 -0
  41. wagtail/admin/templates/wagtailadmin/chooser/browse.html +2 -1
  42. wagtail/admin/templates/wagtailadmin/chooser/tables/parent_page_cell.html +1 -1
  43. wagtail/admin/templates/wagtailadmin/collections/_privacy_switch.html +8 -1
  44. wagtail/admin/templates/wagtailadmin/generic/confirm_delete.html +15 -9
  45. wagtail/admin/templates/wagtailadmin/generic/confirm_unpublish.html +21 -25
  46. wagtail/admin/templates/wagtailadmin/generic/form.html +1 -1
  47. wagtail/admin/templates/wagtailadmin/generic/preview_error.html +3 -0
  48. wagtail/admin/templates/wagtailadmin/generic/revisions/compare.html +63 -76
  49. wagtail/admin/templates/wagtailadmin/pages/_editor_js.html +0 -2
  50. wagtail/admin/templates/wagtailadmin/pages/edit.html +1 -5
  51. wagtail/admin/templates/wagtailadmin/panels/inline_panel_child.html +1 -0
  52. wagtail/admin/templates/wagtailadmin/permissions/includes/collection_member_permissions_form.html +1 -1
  53. wagtail/admin/templates/wagtailadmin/permissions/includes/collection_member_permissions_formset.html +6 -22
  54. wagtail/admin/templates/wagtailadmin/shared/formatted_field.html +2 -2
  55. wagtail/admin/templates/wagtailadmin/shared/header.html +2 -2
  56. wagtail/admin/templates/wagtailadmin/shared/page_status_tag_new.html +32 -39
  57. wagtail/admin/templates/wagtailadmin/shared/revisions/confirm_unschedule.html +13 -17
  58. wagtail/admin/templates/wagtailadmin/shared/side_panels/includes/status/privacy.html +15 -3
  59. wagtail/admin/templates/wagtailadmin/shared/side_panels/preview.html +1 -1
  60. wagtail/admin/templates/wagtailadmin/skeleton.html +4 -2
  61. wagtail/admin/templates/wagtailadmin/workflows/create.html +1 -1
  62. wagtail/admin/templates/wagtailadmin/workflows/edit.html +1 -1
  63. wagtail/admin/templates/wagtailadmin/workflows/includes/workflow_pages_form.html +1 -1
  64. wagtail/admin/templates/wagtailadmin/workflows/includes/workflow_pages_formset.html +6 -23
  65. wagtail/admin/templatetags/wagtailadmin_tags.py +12 -0
  66. wagtail/admin/templatetags/wagtailuserbar.py +2 -3
  67. wagtail/admin/tests/pages/test_create_page.py +110 -1
  68. wagtail/admin/tests/pages/test_edit_page.py +3 -2
  69. wagtail/admin/tests/pages/test_explorer_view.py +18 -0
  70. wagtail/admin/tests/pages/test_page_usage.py +24 -20
  71. wagtail/admin/tests/pages/test_preview.py +69 -1
  72. wagtail/admin/tests/pages/test_revisions.py +40 -6
  73. wagtail/admin/tests/test_account_management.py +39 -1
  74. wagtail/admin/tests/test_audit_log.py +4 -2
  75. wagtail/admin/tests/test_block_preview.py +224 -0
  76. wagtail/admin/tests/test_edit_handlers.py +23 -6
  77. wagtail/admin/tests/test_page_chooser.py +50 -3
  78. wagtail/admin/tests/test_privacy.py +49 -26
  79. wagtail/admin/tests/test_site_summary.py +15 -10
  80. wagtail/admin/tests/test_templatetags.py +19 -0
  81. wagtail/admin/tests/test_userbar.py +82 -1
  82. wagtail/admin/tests/test_views_generic.py +27 -12
  83. wagtail/admin/tests/test_workflows.py +69 -0
  84. wagtail/admin/tests/tests.py +23 -4
  85. wagtail/admin/tests/ui/test_sidebar.py +1 -1
  86. wagtail/admin/tests/viewsets/test_model_viewset.py +15 -13
  87. wagtail/admin/ui/side_panels.py +7 -4
  88. wagtail/admin/urls/__init__.py +6 -0
  89. wagtail/admin/urls/pages.py +1 -1
  90. wagtail/admin/userbar.py +21 -1
  91. wagtail/admin/views/account.py +5 -0
  92. wagtail/admin/views/chooser.py +5 -1
  93. wagtail/admin/views/collections.py +0 -2
  94. wagtail/admin/views/generic/base.py +20 -10
  95. wagtail/admin/views/generic/history.py +0 -1
  96. wagtail/admin/views/generic/models.py +79 -21
  97. wagtail/admin/views/generic/preview.py +50 -1
  98. wagtail/admin/views/mixins.py +4 -2
  99. wagtail/admin/views/pages/bulk_actions/delete.py +11 -23
  100. wagtail/admin/views/pages/bulk_actions/page_bulk_action.py +17 -0
  101. wagtail/admin/views/pages/bulk_actions/publish.py +11 -31
  102. wagtail/admin/views/pages/bulk_actions/unpublish.py +11 -31
  103. wagtail/admin/views/pages/create.py +1 -0
  104. wagtail/admin/views/pages/edit.py +38 -30
  105. wagtail/admin/views/pages/revisions.py +43 -114
  106. wagtail/admin/views/pages/utils.py +0 -1
  107. wagtail/admin/views/tags.py +6 -2
  108. wagtail/admin/views/workflows.py +8 -6
  109. wagtail/admin/viewsets/model.py +0 -4
  110. wagtail/admin/viewsets/pages.py +0 -1
  111. wagtail/admin/widgets/tags.py +1 -0
  112. wagtail/api/v2/tests/test_documents.py +4 -2
  113. wagtail/api/v2/tests/test_images.py +4 -2
  114. wagtail/api/v2/tests/test_pages.py +8 -4
  115. wagtail/blocks/base.py +59 -1
  116. wagtail/blocks/field_block.py +6 -0
  117. wagtail/blocks/list_block.py +4 -0
  118. wagtail/blocks/static_block.py +3 -0
  119. wagtail/blocks/stream_block.py +5 -1
  120. wagtail/blocks/struct_block.py +6 -0
  121. wagtail/compat.py +16 -0
  122. wagtail/contrib/forms/forms.py +27 -7
  123. wagtail/contrib/forms/locale/en/LC_MESSAGES/django.po +2 -2
  124. wagtail/contrib/forms/tests/test_models.py +7 -5
  125. wagtail/contrib/forms/tests/test_views.py +75 -0
  126. wagtail/contrib/frontend_cache/tasks.py +83 -0
  127. wagtail/contrib/frontend_cache/tests.py +47 -32
  128. wagtail/contrib/frontend_cache/utils.py +2 -70
  129. wagtail/contrib/redirects/base_formats.py +2 -2
  130. wagtail/contrib/redirects/locale/ar/LC_MESSAGES/django.mo +0 -0
  131. wagtail/contrib/redirects/locale/ar/LC_MESSAGES/django.po +3 -0
  132. wagtail/contrib/redirects/locale/en/LC_MESSAGES/django.po +24 -37
  133. wagtail/contrib/redirects/templates/wagtailredirects/add.html +1 -24
  134. wagtail/contrib/redirects/templates/wagtailredirects/confirm_delete.html +3 -13
  135. wagtail/contrib/redirects/tests/test_redirects.py +122 -110
  136. wagtail/contrib/redirects/tests/test_signal_handlers.py +75 -69
  137. wagtail/contrib/redirects/urls.py +2 -2
  138. wagtail/contrib/redirects/views.py +35 -73
  139. wagtail/contrib/search_promotions/admin_urls.py +10 -3
  140. wagtail/contrib/search_promotions/forms.py +55 -26
  141. wagtail/contrib/search_promotions/locale/en/LC_MESSAGES/django.po +44 -54
  142. wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/add.html +21 -31
  143. wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/confirm_delete.html +3 -12
  144. wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/edit.html +11 -34
  145. wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/includes/searchpromotion_form.html +1 -0
  146. wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/includes/searchpromotions_formset.js +2 -1
  147. wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/index.html +0 -1
  148. wagtail/contrib/search_promotions/tests.py +814 -13
  149. wagtail/contrib/search_promotions/views/__init__.py +1 -0
  150. wagtail/contrib/search_promotions/views/reports.py +56 -0
  151. wagtail/contrib/search_promotions/views/settings.py +258 -0
  152. wagtail/contrib/search_promotions/wagtail_hooks.py +12 -1
  153. wagtail/contrib/settings/locale/ar/LC_MESSAGES/django.mo +0 -0
  154. wagtail/contrib/settings/locale/ar/LC_MESSAGES/django.po +6 -1
  155. wagtail/contrib/settings/locale/en/LC_MESSAGES/django.po +3 -3
  156. wagtail/contrib/settings/templates/wagtailsettings/edit.html +1 -5
  157. wagtail/contrib/settings/tests/generic/test_admin.py +2 -5
  158. wagtail/contrib/settings/tests/generic/test_register.py +1 -1
  159. wagtail/contrib/settings/tests/site_specific/test_admin.py +2 -5
  160. wagtail/contrib/settings/tests/site_specific/test_register.py +1 -1
  161. wagtail/contrib/settings/views.py +9 -23
  162. wagtail/contrib/simple_translation/locale/en/LC_MESSAGES/django.po +1 -1
  163. wagtail/contrib/styleguide/locale/en/LC_MESSAGES/django.po +1 -1
  164. wagtail/contrib/table_block/locale/en/LC_MESSAGES/django.po +1 -1
  165. wagtail/contrib/table_block/tests.py +4 -1
  166. wagtail/contrib/typed_table_block/blocks.py +3 -0
  167. wagtail/contrib/typed_table_block/locale/en/LC_MESSAGES/django.po +10 -10
  168. wagtail/contrib/typed_table_block/static/typed_table_block/js/typed_table_block.js +1 -1
  169. wagtail/contrib/typed_table_block/tests.py +33 -0
  170. wagtail/documents/locale/en/LC_MESSAGES/django.po +26 -26
  171. wagtail/documents/migrations/0011_add_choose_permissions.py +1 -0
  172. wagtail/documents/models.py +1 -0
  173. wagtail/documents/signal_handlers.py +6 -2
  174. wagtail/documents/static/wagtaildocs/js/add-multiple.js +1 -1
  175. wagtail/documents/templates/wagtaildocs/documents/edit.html +1 -3
  176. wagtail/documents/templates/wagtaildocs/multiple/add.html +7 -1
  177. wagtail/documents/tests/test_admin_views.py +74 -33
  178. wagtail/documents/tests/test_views.py +21 -12
  179. wagtail/documents/views/chooser.py +1 -0
  180. wagtail/documents/views/documents.py +1 -2
  181. wagtail/documents/views/multiple.py +0 -1
  182. wagtail/documents/views/serve.py +9 -2
  183. wagtail/documents/wagtail_hooks.py +6 -1
  184. wagtail/embeds/locale/en/LC_MESSAGES/django.po +1 -1
  185. wagtail/embeds/oembed_providers.py +0 -64
  186. wagtail/fields.py +3 -0
  187. wagtail/images/apps.py +2 -1
  188. wagtail/images/blocks.py +6 -2
  189. wagtail/images/forms.py +40 -3
  190. wagtail/images/locale/ar/LC_MESSAGES/django.mo +0 -0
  191. wagtail/images/locale/ar/LC_MESSAGES/django.po +4 -0
  192. wagtail/images/locale/en/LC_MESSAGES/django.po +49 -49
  193. wagtail/images/migrations/0023_add_choose_permissions.py +1 -0
  194. wagtail/images/rich_text/contentstate.py +1 -0
  195. wagtail/images/rich_text/editor_html.py +1 -0
  196. wagtail/images/signal_handlers.py +17 -10
  197. wagtail/images/static/wagtailimages/js/add-multiple.js +1 -1
  198. wagtail/images/static/wagtailimages/js/image-block.js +1 -1
  199. wagtail/images/static/wagtailimages/js/image-chooser-telepath.js +1 -1
  200. wagtail/images/static/wagtailimages/js/image-chooser.js +1 -1
  201. wagtail/images/static/wagtailimages/js/image-url-generator.js +1 -1
  202. wagtail/images/static/wagtailimages/js/vendor/jquery.fileupload-image.js +1 -1
  203. wagtail/images/tasks.py +18 -0
  204. wagtail/images/templates/wagtailimages/images/edit.html +1 -3
  205. wagtail/images/templates/wagtailimages/images/url_generator.html +1 -1
  206. wagtail/images/templates/wagtailimages/multiple/add.html +7 -2
  207. wagtail/images/templates/wagtailimages/widgets/image_chooser.html +1 -1
  208. wagtail/images/tests/test_admin_views.py +53 -29
  209. wagtail/images/tests/test_blocks.py +3 -2
  210. wagtail/images/tests/test_models.py +12 -10
  211. wagtail/images/tests/tests.py +10 -0
  212. wagtail/images/views/chooser.py +1 -0
  213. wagtail/images/views/images.py +1 -3
  214. wagtail/images/views/multiple.py +0 -1
  215. wagtail/images/views/serve.py +18 -2
  216. wagtail/images/widgets.py +3 -0
  217. wagtail/locale/en/LC_MESSAGES/django.po +228 -216
  218. wagtail/locales/locale/en/LC_MESSAGES/django.po +1 -1
  219. wagtail/management/commands/publish_scheduled.py +1 -1
  220. wagtail/migrations/0087_alter_grouppagepermission_unique_together_and_more.py +16 -8
  221. wagtail/models/__init__.py +300 -119
  222. wagtail/models/i18n.py +2 -2
  223. wagtail/models/panels.py +37 -0
  224. wagtail/models/sites.py +7 -6
  225. wagtail/permission_policies/pages.py +2 -2
  226. wagtail/project_template/project_name/settings/base.py +4 -0
  227. wagtail/project_template/requirements.txt +1 -1
  228. wagtail/query.py +145 -0
  229. wagtail/search/backends/database/mysql/mysql.py +25 -17
  230. wagtail/search/backends/database/postgres/postgres.py +44 -83
  231. wagtail/search/backends/database/sqlite/sqlite.py +25 -17
  232. wagtail/search/backends/elasticsearch7.py +4 -0
  233. wagtail/search/locale/en/LC_MESSAGES/django.po +1 -1
  234. wagtail/search/query.py +8 -2
  235. wagtail/search/signal_handlers.py +6 -9
  236. wagtail/search/tasks.py +10 -0
  237. wagtail/search/tests/test_elasticsearch7_backend.py +21 -0
  238. wagtail/search/tests/test_index_functions.py +10 -6
  239. wagtail/search/tests/test_postgres_backend.py +0 -14
  240. wagtail/signal_handlers.py +5 -20
  241. wagtail/sites/locale/en/LC_MESSAGES/django.po +1 -1
  242. wagtail/snippets/locale/en/LC_MESSAGES/django.po +3 -13
  243. wagtail/snippets/tests/test_preview.py +5 -0
  244. wagtail/snippets/tests/test_snippets.py +100 -45
  245. wagtail/snippets/tests/test_usage.py +29 -24
  246. wagtail/snippets/tests/test_viewset.py +1 -1
  247. wagtail/snippets/views/snippets.py +0 -12
  248. wagtail/tasks.py +41 -0
  249. wagtail/templates/wagtailcore/shared/block_preview.html +29 -0
  250. wagtail/test/earlypage/__init__.py +0 -0
  251. wagtail/test/earlypage/migrations/0001_initial.py +37 -0
  252. wagtail/test/earlypage/migrations/__init__.py +0 -0
  253. wagtail/test/earlypage/models.py +14 -0
  254. wagtail/test/settings.py +3 -0
  255. wagtail/test/testapp/fixtures/test.json +7 -0
  256. wagtail/test/testapp/fixtures/test_specific.json +6 -3
  257. wagtail/test/testapp/models.py +58 -44
  258. wagtail/test/testapp/templates/tests/custom_block_preview.html +16 -0
  259. wagtail/test/testapp/templates/tests/static_block_preview.html +5 -0
  260. wagtail/test/testapp/wagtail_hooks.py +9 -0
  261. wagtail/tests/test_blocks.py +189 -2
  262. wagtail/tests/test_hooks.py +166 -1
  263. wagtail/tests/test_management_commands.py +54 -13
  264. wagtail/tests/test_page_allowed_http_methods.py +32 -0
  265. wagtail/tests/test_page_model.py +68 -0
  266. wagtail/tests/test_page_privacy.py +10 -0
  267. wagtail/tests/test_page_queryset.py +79 -0
  268. wagtail/tests/test_reference_index.py +84 -75
  269. wagtail/tests/test_streamfield.py +30 -0
  270. wagtail/tests/test_utils.py +61 -0
  271. wagtail/users/forms.py +2 -9
  272. wagtail/users/locale/en/LC_MESSAGES/django.po +17 -17
  273. wagtail/users/templates/wagtailusers/groups/create.html +0 -5
  274. wagtail/users/templates/wagtailusers/groups/includes/page_permissions_form.html +1 -1
  275. wagtail/users/templates/wagtailusers/groups/includes/page_permissions_formset.html +6 -6
  276. wagtail/users/tests/test_admin_views.py +96 -4
  277. wagtail/users/tests/test_utils.py +76 -0
  278. wagtail/users/utils.py +43 -11
  279. wagtail/utils/setup.py +2 -2
  280. wagtail/utils/templates.py +26 -0
  281. wagtail/utils/widgets.py +1 -0
  282. wagtail/views.py +9 -1
  283. wagtail/wagtail_hooks.py +67 -29
  284. {wagtail-6.3.2.dist-info → wagtail-6.4rc1.dist-info}/METADATA +2 -2
  285. {wagtail-6.3.2.dist-info → wagtail-6.4rc1.dist-info}/RECORD +289 -276
  286. wagtail/admin/static/wagtailadmin/js/expanding-formset.js +0 -1
  287. wagtail/admin/static/wagtailadmin/js/vendor/rangy-core.js +0 -1
  288. wagtail/admin/static/wagtailadmin/js/vendor/uuidv4.min.js +0 -1
  289. wagtail/contrib/search_promotions/views.py +0 -323
  290. wagtail/images/static/wagtailimages/js/vendor/canvas-to-blob.min.js +0 -1
  291. wagtail/users/static/wagtailusers/js/group-form.js +0 -1
  292. wagtail/users/templates/wagtailusers/groups/includes/group_form_js.html +0 -3
  293. {wagtail-6.3.2.dist-info → wagtail-6.4rc1.dist-info}/LICENSE +0 -0
  294. {wagtail-6.3.2.dist-info → wagtail-6.4rc1.dist-info}/WHEEL +0 -0
  295. {wagtail-6.3.2.dist-info → wagtail-6.4rc1.dist-info}/entry_points.txt +0 -0
  296. {wagtail-6.3.2.dist-info → wagtail-6.4rc1.dist-info}/top_level.txt +0 -0
@@ -1,34 +1,41 @@
1
1
  from django.conf import settings
2
2
  from django.db import transaction
3
- from django.db.models.signals import post_delete, pre_save
3
+ from django.db.models.signals import post_delete, post_save
4
4
 
5
5
  from wagtail.images import get_image_model
6
+ from wagtail.tasks import delete_file_from_storage_task
7
+
8
+ from .tasks import set_image_focal_point_task
6
9
 
7
10
 
8
11
  def post_delete_file_cleanup(instance, **kwargs):
9
- # Pass false so FileField doesn't save the model.
10
- transaction.on_commit(lambda: instance.file.delete(False))
12
+ transaction.on_commit(
13
+ lambda: delete_file_from_storage_task.enqueue(
14
+ instance.file.storage.deconstruct(), instance.file.name
15
+ )
16
+ )
11
17
 
12
18
 
13
19
  def post_delete_purge_rendition_cache(instance, **kwargs):
14
20
  instance.purge_from_cache()
15
21
 
16
22
 
17
- def pre_save_image_feature_detection(instance, **kwargs):
23
+ def post_save_image_feature_detection(instance, **kwargs):
18
24
  if getattr(settings, "WAGTAILIMAGES_FEATURE_DETECTION_ENABLED", False):
19
25
  # Make sure the image is not from a fixture
20
- if kwargs["raw"] is False:
21
- # Make sure the image doesn't already have a focal point
22
- if not instance.has_focal_point():
23
- # Set the focal point
24
- instance.set_focal_point(instance.get_suggested_focal_point())
26
+ # and the image doesn't already have a focal point
27
+ if kwargs["raw"] is False and not instance.has_focal_point():
28
+ # Set the focal point
29
+ set_image_focal_point_task.enqueue(
30
+ instance._meta.app_label, instance._meta.model_name, instance.pk
31
+ )
25
32
 
26
33
 
27
34
  def register_signal_handlers():
28
35
  Image = get_image_model()
29
36
  Rendition = Image.get_rendition_model()
30
37
 
31
- pre_save.connect(pre_save_image_feature_detection, sender=Image)
38
+ post_save.connect(post_save_image_feature_detection, sender=Image)
32
39
  post_delete.connect(post_delete_file_cleanup, sender=Image)
33
40
  post_delete.connect(post_delete_file_cleanup, sender=Rendition)
34
41
  post_delete.connect(post_delete_purge_rendition_cache, sender=Rendition)
@@ -1 +1 @@
1
- $((function(){$(document).on("drop dragover",(function(e){e.preventDefault()})),$("#fileupload").fileupload({dataType:"html",sequentialUploads:!0,dropZone:$(".drop-zone"),previewMinWidth:150,previewMaxWidth:150,previewMinHeight:150,previewMaxHeight:150,add:function(e,t){var a=$(this),o=a.data("blueimp-fileupload")||a.data("fileupload"),s=$($("#upload-list-item").html()).addClass("upload-uploading"),i=o.options;$("#upload-list").append(s),t.context=s,t.process((function(){return a.fileupload("process",t)})).always((function(){t.context.removeClass("processing"),t.context.find(".left").each((function(e,a){$(a).append(escapeHtml(t.files[e].name))})),t.context.find(".preview .thumb").each((function(e,a){$(a).find(".icon").remove(),$(a).append(t.files[e].preview)}))})).done((function(){t.context.find(".start").prop("disabled",!1),!1!==o._trigger("added",e,t)&&(i.autoUpload||t.autoUpload)&&!1!==t.autoUpload&&t.submit()})).fail((function(){t.files.error&&t.context.each((function(e){var a=t.files[e].error;a&&$(this).find(".error_messages").html(a)}))}))},processfail:function(e,t){$(t.context).removeClass("upload-uploading").addClass("upload-failure")},progress:function(e,t){if(e.isDefaultPrevented())return!1;var a=Math.floor(t.loaded/t.total*100);t.context.each((function(){$(this).find(".progress").addClass("active").attr("aria-valuenow",a).find(".bar").css("width",a+"%").html(a+"%")}))},progressall:function(e,t){var a=parseInt(t.loaded/t.total*100,10);$("#overall-progress").addClass("active").attr("aria-valuenow",a).find(".bar").css("width",a+"%").html(a+"%"),a>=100&&$("#overall-progress").removeClass("active").find(".bar").css("width","0%")},formData:function(e){var t=this.files[0].name,a={title:t.replace(/\.[^.]+$/,"")};return e.get(0).dispatchEvent(new CustomEvent("wagtail:images-upload",{bubbles:!0,cancelable:!0,detail:{data:a,filename:t,maxTitleLength:this.maxTitleLength}}))?e.serializeArray().concat({name:"title",value:a.title}):e.serializeArray()},done:function(e,t){var a=$(t.context),o=JSON.parse(t.result);o.success?o.duplicate?(a.addClass("upload-duplicate"),$(".right",a).append(o.confirm_duplicate_upload),$(".confirm-duplicate-upload",a).on("click",".confirm-upload",(function(e){e.preventDefault(),$(this).closest(".confirm-duplicate-upload").remove(),$(".right",a).append(o.form)}))):(a.addClass("upload-success"),$(".right",a).append(o.form)):(a.addClass("upload-failure"),$(".right .error_messages",a).append(o.error_message))},fail:function(e,t){var a=$(t.context),o=$(".server-error",a);$(".error-text",o).text(t.errorThrown),$(".error-code",o).text(t.jqXHR.status),a.addClass("upload-server-error")},always:function(e,t){$(t.context).removeClass("upload-uploading").addClass("upload-complete")}}),$("#upload-list").on("submit","form",(function(e){var t=$(this),a=new FormData(this),o=t.closest("#upload-list > li");e.preventDefault(),$.ajax({contentType:!1,data:a,processData:!1,type:"POST",url:this.action}).done((function(e){if(e.success){var a=$(".status-msg.update-success").first().text();document.dispatchEvent(new CustomEvent("w-messages:add",{detail:{clear:!0,text:a,type:"success"}})),o.slideUp((function(){$(this).remove()}))}else t.replaceWith(e.form)}))})),$("#upload-list").on("click",".delete",(function(e){var t=$(this).closest("form"),a=t.closest("#upload-list > li");e.preventDefault();var o=$('input[name="csrfmiddlewaretoken"]',t).val();$.post(this.href,{csrfmiddlewaretoken:o},(function(e){e.success&&a.slideUp((function(){$(this).remove()}))}))}))}));
1
+ $((function(){$("#fileupload").fileupload({dataType:"html",sequentialUploads:!0,dropZone:$(".drop-zone"),previewMinWidth:150,previewMaxWidth:150,previewMinHeight:150,previewMaxHeight:150,add:function(e,t){var a=$(this),o=a.data("blueimp-fileupload")||a.data("fileupload"),s=$($("#upload-list-item").html()).addClass("upload-uploading"),i=o.options;$("#upload-list").append(s),t.context=s,t.process((function(){return a.fileupload("process",t)})).always((function(){t.context.removeClass("processing"),t.context.find(".left").each((function(e,a){$(a).append(escapeHtml(t.files[e].name))})),t.context.find(".preview .thumb").each((function(e,a){$(a).find(".icon").remove(),$(a).append(t.files[e].preview)}))})).done((function(){t.context.find(".start").prop("disabled",!1),!1!==o._trigger("added",e,t)&&(i.autoUpload||t.autoUpload)&&!1!==t.autoUpload&&t.submit()})).fail((function(){t.files.error&&t.context.each((function(e){var a=t.files[e].error;a&&$(this).find(".error_messages").html(a)}))}))},processfail:function(e,t){$(t.context).removeClass("upload-uploading").addClass("upload-failure")},progress:function(e,t){if(e.isDefaultPrevented())return!1;var a=Math.floor(t.loaded/t.total*100);t.context.each((function(){$(this).find(".progress").addClass("active").attr("aria-valuenow",a).find(".bar").css("width",a+"%").html(a+"%")}))},progressall:function(e,t){var a=parseInt(t.loaded/t.total*100,10);$("#overall-progress").addClass("active").attr("aria-valuenow",a).find(".bar").css("width",a+"%").html(a+"%"),a>=100&&$("#overall-progress").removeClass("active").find(".bar").css("width","0%")},formData:function(e){var t=this.files[0].name,a={title:t.replace(/\.[^.]+$/,"")};return e.get(0).dispatchEvent(new CustomEvent("wagtail:images-upload",{bubbles:!0,cancelable:!0,detail:{data:a,filename:t,maxTitleLength:this.maxTitleLength}}))?e.serializeArray().concat({name:"title",value:a.title}):e.serializeArray()},done:function(e,t){var a=$(t.context),o=JSON.parse(t.result);o.success?o.duplicate?(a.addClass("upload-duplicate"),$(".right",a).append(o.confirm_duplicate_upload),$(".confirm-duplicate-upload",a).on("click",".confirm-upload",(function(e){e.preventDefault(),$(this).closest(".confirm-duplicate-upload").remove(),$(".right",a).append(o.form)}))):(a.addClass("upload-success"),$(".right",a).append(o.form)):(a.addClass("upload-failure"),$(".right .error_messages",a).append(o.error_message))},fail:function(e,t){var a=$(t.context),o=$(".server-error",a);$(".error-text",o).text(t.errorThrown),$(".error-code",o).text(t.jqXHR.status),a.addClass("upload-server-error")},always:function(e,t){$(t.context).removeClass("upload-uploading").addClass("upload-complete")}}),$("#upload-list").on("submit","form",(function(e){var t=$(this),a=new FormData(this),o=t.closest("#upload-list > li");e.preventDefault(),$.ajax({contentType:!1,data:a,processData:!1,type:"POST",url:this.action}).done((function(e){if(e.success){var a=$(".status-msg.update-success").first().text();document.dispatchEvent(new CustomEvent("w-messages:add",{detail:{clear:!0,text:a,type:"success"}})),o.slideUp((function(){$(this).remove()}))}else t.replaceWith(e.form)}))})),$("#upload-list").on("click",".delete",(function(e){var t=$(this).closest("form"),a=t.closest("#upload-list > li");e.preventDefault();var o=$('input[name="csrfmiddlewaretoken"]',t).val();$.post(this.href,{csrfmiddlewaretoken:o},(function(e){e.success&&a.slideUp((function(){$(this).remove()}))}))}))}));
@@ -1 +1 @@
1
- (()=>{class e extends window.wagtailStreamField.blocks.StructBlockDefinition{render(e,t,d,i){const n=super.render(e,t,d,i),r=document.getElementById(`${t}-alt_text`),a=document.getElementById(`${t}-decorative`),l=()=>{a.checked?r.setAttribute("disabled",!0):r.removeAttribute("disabled")};return l(),a.addEventListener("change",l),n}}window.telepath.register("wagtail.images.blocks.ImageBlock",e)})();
1
+ (()=>{class e extends window.wagtailStreamField.blocks.StructBlockDefinition{render(e,t,a,l){const d=super.render(e,t,a,l),i=document.getElementById(`${t}-alt_text`),n=document.getElementById(`${t}-decorative`),c=()=>{n.checked?i.setAttribute("disabled",!0):i.removeAttribute("disabled")};c(),n.addEventListener("change",c);const o=d.childBlocks.image.widget;let r=a?.image?.default_alt_text||"";return o.on("chosen",(e=>{i.value===r&&(i.value=e.default_alt_text),r=e.default_alt_text})),d}}window.telepath.register("wagtail.images.blocks.ImageBlock",e)})();
@@ -1 +1 @@
1
- (()=>{"use strict";var e,t={7199:(e,t,r)=>{var i=r(9465);class o extends i.y{chooserModalClass=ImageChooserModal;initHTMLElements(e){super.initHTMLElements(e),this.previewImage=this.chooserElement.querySelector("[data-chooser-image]")}getStateFromHTML(){const e=super.getStateFromHTML();return e&&(e.preview={url:this.previewImage.getAttribute("src"),width:this.previewImage.getAttribute("width"),height:this.previewImage.getAttribute("height")}),e}renderState(e){super.renderState(e),this.previewImage.setAttribute("src",e.preview.url),this.previewImage.setAttribute("width",e.preview.width)}}class a extends i._{widgetClass=o;chooserModalClass=ImageChooserModal}window.telepath.register("wagtail.images.widgets.ImageChooser",a)},1669:e=>{e.exports=jQuery}},r={};function i(e){var o=r[e];if(void 0!==o)return o.exports;var a=r[e]={exports:{}};return t[e](a,a.exports,i),a.exports}i.m=t,e=[],i.O=(t,r,o,a)=>{if(!r){var s=1/0;for(u=0;u<e.length;u++){for(var[r,o,a]=e[u],n=!0,l=0;l<r.length;l++)(!1&a||s>=a)&&Object.keys(i.O).every((e=>i.O[e](r[l])))?r.splice(l--,1):(n=!1,a<s&&(s=a));if(n){e.splice(u--,1);var h=o();void 0!==h&&(t=h)}}return t}a=a||0;for(var u=e.length;u>0&&e[u-1][2]>a;u--)e[u]=e[u-1];e[u]=[r,o,a]},i.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return i.d(t,{a:t}),t},i.d=(e,t)=>{for(var r in t)i.o(t,r)&&!i.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},i.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),i.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),i.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.j=246,(()=>{var e={246:0};i.O.j=t=>0===e[t];var t=(t,r)=>{var o,a,[s,n,l]=r,h=0;if(s.some((t=>0!==e[t]))){for(o in n)i.o(n,o)&&(i.m[o]=n[o]);if(l)var u=l(i)}for(t&&t(r);h<s.length;h++)a=s[h],i.o(e,a)&&e[a]&&e[a][0](),e[a]=0;return i.O(u)},r=globalThis.webpackChunkwagtail=globalThis.webpackChunkwagtail||[];r.forEach(t.bind(null,0)),r.push=t.bind(null,r.push.bind(r))})();var o=i.O(void 0,[321],(()=>i(7199)));o=i.O(o)})();
1
+ (()=>{"use strict";var e,t={7199:(e,t,r)=>{var i=r(9465);class a extends i.y{chooserModalClass=ImageChooserModal;initHTMLElements(e){super.initHTMLElements(e),this.previewImage=this.chooserElement.querySelector("[data-chooser-image]")}getStateFromHTML(){const e=super.getStateFromHTML();return e&&(e.preview={url:this.previewImage.getAttribute("src"),width:this.previewImage.getAttribute("width"),height:this.previewImage.getAttribute("height")},e.default_alt_text=this.previewImage.getAttribute("data-default-alt-text")),e}renderState(e){super.renderState(e),this.previewImage.setAttribute("src",e.preview.url),this.previewImage.setAttribute("width",e.preview.width),this.previewImage.setAttribute("data-default-alt-text",e.default_alt_text)}}class o extends i._{widgetClass=a;chooserModalClass=ImageChooserModal}window.telepath.register("wagtail.images.widgets.ImageChooser",o)},1669:e=>{e.exports=jQuery}},r={};function i(e){var a=r[e];if(void 0!==a)return a.exports;var o=r[e]={exports:{}};return t[e](o,o.exports,i),o.exports}i.m=t,e=[],i.O=(t,r,a,o)=>{if(!r){var s=1/0;for(h=0;h<e.length;h++){for(var[r,a,o]=e[h],l=!0,n=0;n<r.length;n++)(!1&o||s>=o)&&Object.keys(i.O).every((e=>i.O[e](r[n])))?r.splice(n--,1):(l=!1,o<s&&(s=o));if(l){e.splice(h--,1);var u=a();void 0!==u&&(t=u)}}return t}o=o||0;for(var h=e.length;h>0&&e[h-1][2]>o;h--)e[h]=e[h-1];e[h]=[r,a,o]},i.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return i.d(t,{a:t}),t},i.d=(e,t)=>{for(var r in t)i.o(t,r)&&!i.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},i.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),i.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),i.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.j=246,(()=>{var e={246:0};i.O.j=t=>0===e[t];var t=(t,r)=>{var a,o,[s,l,n]=r,u=0;if(s.some((t=>0!==e[t]))){for(a in l)i.o(l,a)&&(i.m[a]=l[a]);if(n)var h=n(i)}for(t&&t(r);u<s.length;u++)o=s[u],i.o(e,o)&&e[o]&&e[o][0](),e[o]=0;return i.O(h)},r=globalThis.webpackChunkwagtail=globalThis.webpackChunkwagtail||[];r.forEach(t.bind(null,0)),r.push=t.bind(null,r.push.bind(r))})();var a=i.O(void 0,[321],(()=>i(7199)));a=i.O(a)})();
@@ -1 +1 @@
1
- (()=>{"use strict";var e,t={4023:(e,t,r)=>{var i=r(9465);class o extends i.y{chooserModalClass=ImageChooserModal;initHTMLElements(e){super.initHTMLElements(e),this.previewImage=this.chooserElement.querySelector("[data-chooser-image]")}getStateFromHTML(){const e=super.getStateFromHTML();return e&&(e.preview={url:this.previewImage.getAttribute("src"),width:this.previewImage.getAttribute("width"),height:this.previewImage.getAttribute("height")}),e}renderState(e){super.renderState(e),this.previewImage.setAttribute("src",e.preview.url),this.previewImage.setAttribute("width",e.preview.width)}}window.ImageChooser=o},1669:e=>{e.exports=jQuery}},r={};function i(e){var o=r[e];if(void 0!==o)return o.exports;var a=r[e]={exports:{}};return t[e](a,a.exports,i),a.exports}i.m=t,e=[],i.O=(t,r,o,a)=>{if(!r){var n=1/0;for(h=0;h<e.length;h++){for(var[r,o,a]=e[h],s=!0,l=0;l<r.length;l++)(!1&a||n>=a)&&Object.keys(i.O).every((e=>i.O[e](r[l])))?r.splice(l--,1):(s=!1,a<n&&(n=a));if(s){e.splice(h--,1);var u=o();void 0!==u&&(t=u)}}return t}a=a||0;for(var h=e.length;h>0&&e[h-1][2]>a;h--)e[h]=e[h-1];e[h]=[r,o,a]},i.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return i.d(t,{a:t}),t},i.d=(e,t)=>{for(var r in t)i.o(t,r)&&!i.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},i.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),i.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),i.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.j=306,(()=>{var e={306:0};i.O.j=t=>0===e[t];var t=(t,r)=>{var o,a,[n,s,l]=r,u=0;if(n.some((t=>0!==e[t]))){for(o in s)i.o(s,o)&&(i.m[o]=s[o]);if(l)var h=l(i)}for(t&&t(r);u<n.length;u++)a=n[u],i.o(e,a)&&e[a]&&e[a][0](),e[a]=0;return i.O(h)},r=globalThis.webpackChunkwagtail=globalThis.webpackChunkwagtail||[];r.forEach(t.bind(null,0)),r.push=t.bind(null,r.push.bind(r))})();var o=i.O(void 0,[321],(()=>i(4023)));o=i.O(o)})();
1
+ (()=>{"use strict";var e,t={4023:(e,t,r)=>{var i=r(9465);class a extends i.y{chooserModalClass=ImageChooserModal;initHTMLElements(e){super.initHTMLElements(e),this.previewImage=this.chooserElement.querySelector("[data-chooser-image]")}getStateFromHTML(){const e=super.getStateFromHTML();return e&&(e.preview={url:this.previewImage.getAttribute("src"),width:this.previewImage.getAttribute("width"),height:this.previewImage.getAttribute("height")},e.default_alt_text=this.previewImage.getAttribute("data-default-alt-text")),e}renderState(e){super.renderState(e),this.previewImage.setAttribute("src",e.preview.url),this.previewImage.setAttribute("width",e.preview.width),this.previewImage.setAttribute("data-default-alt-text",e.default_alt_text)}}window.ImageChooser=a},1669:e=>{e.exports=jQuery}},r={};function i(e){var a=r[e];if(void 0!==a)return a.exports;var o=r[e]={exports:{}};return t[e](o,o.exports,i),o.exports}i.m=t,e=[],i.O=(t,r,a,o)=>{if(!r){var n=1/0;for(h=0;h<e.length;h++){for(var[r,a,o]=e[h],s=!0,l=0;l<r.length;l++)(!1&o||n>=o)&&Object.keys(i.O).every((e=>i.O[e](r[l])))?r.splice(l--,1):(s=!1,o<n&&(n=o));if(s){e.splice(h--,1);var u=a();void 0!==u&&(t=u)}}return t}o=o||0;for(var h=e.length;h>0&&e[h-1][2]>o;h--)e[h]=e[h-1];e[h]=[r,a,o]},i.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return i.d(t,{a:t}),t},i.d=(e,t)=>{for(var r in t)i.o(t,r)&&!i.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},i.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),i.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),i.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.j=306,(()=>{var e={306:0};i.O.j=t=>0===e[t];var t=(t,r)=>{var a,o,[n,s,l]=r,u=0;if(n.some((t=>0!==e[t]))){for(a in s)i.o(s,a)&&(i.m[a]=s[a]);if(l)var h=l(i)}for(t&&t(r);u<n.length;u++)o=n[u],i.o(e,o)&&e[o]&&e[o][0](),e[o]=0;return i.O(h)},r=globalThis.webpackChunkwagtail=globalThis.webpackChunkwagtail||[];r.forEach(t.bind(null,0)),r.push=t.bind(null,r.push.bind(r))})();var a=i.O(void 0,[321],(()=>i(4023)));a=i.O(a)})();
@@ -1 +1 @@
1
- $((function(){$("[data-generator-url]").each((function(){var i=$(this),d=i.find("form"),e=d.find("select#id_filter_method"),a=d.find("input#id_width"),l=d.find("input#id_height"),r=d.find("input#id_closeness"),n=i.find("#result-url"),o=i.find(".loading-mask"),p=i.find("img.preview"),s=i.data("generatorUrl");function t(){var i=e.val();o.addClass("loading"),"original"===i?(a.prop("disabled",!0),l.prop("disabled",!0),r.prop("disabled",!0)):"width"===i?(a.prop("disabled",!1),l.prop("disabled",!0),r.prop("disabled",!0),i+="-"+a.val()):"height"===i?(a.prop("disabled",!0),l.prop("disabled",!1),r.prop("disabled",!0),i+="-"+l.val()):"min"!==i&&"max"!==i&&"fill"!==i||(a.prop("disabled",!1),l.prop("disabled",!1),"fill"===i?(r.prop("disabled",!1),i+="-"+a.val()+"x"+l.val()+"-c"+r.val()):(r.prop("disabled",!0),i+="-"+a.val()+"x"+l.val())),$.getJSON(s.replace("__filterspec__",i)).done((function(i){n.val(i.url),p.attr("src",i.preview_url),o.removeClass("loading")})).fail((function(i){n.val(i.responseJSON.error),p.attr("src",""),o.removeClass("loading")}))}d.on("change",$.debounce(500,t)),d.on("keyup",$.debounce(500,t)),t()}))}));
1
+ $((function(){$("[data-generator-url]").each((function(){var i=$(this),e=i.find("form"),n=e.find("select#id_filter_method"),a=e.find("input#id_width"),l=e.find("input#id_height"),r=e.find("input#id_closeness"),t=i.find("#result-url"),d=i.find(".loading-mask"),o=i.find("img.preview"),f=i.data("generatorUrl");function s(){var i=n.val();d.addClass("loading"),"width"===i?i+="-"+a.val():"height"===i?i+="-"+l.val():"min"!==i&&"max"!==i&&"fill"!==i||(i+="fill"===i?"-"+a.val()+"x"+l.val()+"-c"+r.val():"-"+a.val()+"x"+l.val()),$.getJSON(f.replace("__filterspec__",i)).done((function(i){t.val(i.url),o.attr("src",i.preview_url),d.removeClass("loading")})).fail((function(i){t.val(i.responseJSON.error),o.attr("src",""),d.removeClass("loading")}))}e.on("change",$.debounce(500,s)),e.on("keyup",$.debounce(500,s)),s()}))}));
@@ -1 +1 @@
1
- !function(e){"use strict";"function"==typeof define&&define.amd?define(["jquery","load-image","load-image-meta","load-image-exif","load-image-ios","canvas-to-blob","./jquery.fileupload-process"],e):e(window.jQuery,window.loadImage)}((function(e,i){"use strict";e.blueimp.fileupload.prototype.options.processQueue.unshift({action:"loadImageMetaData",disableImageHead:"@",disableExif:"@",disableExifThumbnail:"@",disableExifSub:"@",disableExifGps:"@",disabled:"@disableImageMetaDataLoad"},{action:"loadImage",prefix:!0,fileTypes:"@",maxFileSize:"@",noRevoke:"@",disabled:"@disableImageLoad"},{action:"resizeImage",prefix:"image",maxWidth:"@",maxHeight:"@",minWidth:"@",minHeight:"@",crop:"@",orientation:"@",forceResize:"@",disabled:"@disableImageResize"},{action:"saveImage",quality:"@imageQuality",type:"@imageType",disabled:"@disableImageResize"},{action:"saveImageMetaData",disabled:"@disableImageMetaDataSave"},{action:"resizeImage",prefix:"preview",maxWidth:"@",maxHeight:"@",minWidth:"@",minHeight:"@",crop:"@",orientation:"@",thumbnail:"@",canvas:"@",disabled:"@disableImagePreview"},{action:"setImage",name:"@imagePreviewName",disabled:"@disableImagePreview"},{action:"deleteImageReferences",disabled:"@disableImageReferencesDeletion"}),e.widget("blueimp.fileupload",e.blueimp.fileupload,{options:{loadImageFileTypes:/^image\/(gif|jpeg|png|svg\+xml)$/,loadImageMaxFileSize:1e7,imageMaxWidth:1920,imageMaxHeight:1080,imageOrientation:!1,imageCrop:!1,disableImageResize:!0,previewMaxWidth:80,previewMaxHeight:80,previewOrientation:!0,previewThumbnail:!0,previewCrop:!1,previewCanvas:!0},processActions:{loadImage:function(a,t){if(t.disabled)return a;var n=this,s=a.files[a.index],r=e.Deferred();return"number"===e.type(t.maxFileSize)&&s.size>t.maxFileSize||t.fileTypes&&!t.fileTypes.test(s.type)||!i(s,(function(e){e.src&&(a.img=e),r.resolveWith(n,[a])}),t)?a:r.promise()},resizeImage:function(a,t){if(t.disabled||!a.canvas&&!a.img)return a;t=e.extend({canvas:!0},t);var n,s=this,r=e.Deferred(),d=t.canvas&&a.canvas||a.img,o=function(e){e&&(e.width!==d.width||e.height!==d.height||t.forceResize)&&(a[e.getContext?"canvas":"img"]=e),a.preview=e,r.resolveWith(s,[a])};if(a.exif){if(!0===t.orientation&&(t.orientation=a.exif.get("Orientation")),t.thumbnail&&(n=a.exif.get("Thumbnail")))return i(n,o,t),r.promise();a.orientation?delete t.orientation:a.orientation=t.orientation}return d?(o(i.scale(d,t)),r.promise()):a},saveImage:function(i,a){if(!i.canvas||a.disabled)return i;var t=this,n=i.files[i.index],s=e.Deferred();return i.canvas.toBlob?(i.canvas.toBlob((function(e){e.name||(n.type===e.type?e.name=n.name:n.name&&(e.name=n.name.replace(/\..+$/,"."+e.type.substr(6)))),n.type!==e.type&&delete i.imageHead,i.files[i.index]=e,s.resolveWith(t,[i])}),a.type||n.type,a.quality),s.promise()):i},loadImageMetaData:function(a,t){if(t.disabled)return a;var n=this,s=e.Deferred();return i.parseMetaData(a.files[a.index],(function(i){e.extend(a,i),s.resolveWith(n,[a])}),t),s.promise()},saveImageMetaData:function(e,i){if(!(e.imageHead&&e.canvas&&e.canvas.toBlob)||i.disabled)return e;var a=e.files[e.index],t=new Blob([e.imageHead,this._blobSlice.call(a,20)],{type:a.type});return t.name=a.name,e.files[e.index]=t,e},setImage:function(e,i){return e.preview&&!i.disabled&&(e.files[e.index][i.name||"preview"]=e.preview),e},deleteImageReferences:function(e,i){return i.disabled||(delete e.img,delete e.canvas,delete e.preview,delete e.imageHead),e}}})}));
1
+ !function(e){"use strict";"function"==typeof define&&define.amd?define(["jquery","load-image","load-image-meta","load-image-exif","load-image-ios","./jquery.fileupload-process"],e):e(window.jQuery,window.loadImage)}((function(e,i){"use strict";e.blueimp.fileupload.prototype.options.processQueue.unshift({action:"loadImageMetaData",disableImageHead:"@",disableExif:"@",disableExifThumbnail:"@",disableExifSub:"@",disableExifGps:"@",disabled:"@disableImageMetaDataLoad"},{action:"loadImage",prefix:!0,fileTypes:"@",maxFileSize:"@",noRevoke:"@",disabled:"@disableImageLoad"},{action:"resizeImage",prefix:"image",maxWidth:"@",maxHeight:"@",minWidth:"@",minHeight:"@",crop:"@",orientation:"@",forceResize:"@",disabled:"@disableImageResize"},{action:"saveImage",quality:"@imageQuality",type:"@imageType",disabled:"@disableImageResize"},{action:"saveImageMetaData",disabled:"@disableImageMetaDataSave"},{action:"resizeImage",prefix:"preview",maxWidth:"@",maxHeight:"@",minWidth:"@",minHeight:"@",crop:"@",orientation:"@",thumbnail:"@",canvas:"@",disabled:"@disableImagePreview"},{action:"setImage",name:"@imagePreviewName",disabled:"@disableImagePreview"},{action:"deleteImageReferences",disabled:"@disableImageReferencesDeletion"}),e.widget("blueimp.fileupload",e.blueimp.fileupload,{options:{loadImageFileTypes:/^image\/(gif|jpeg|png|svg\+xml)$/,loadImageMaxFileSize:1e7,imageMaxWidth:1920,imageMaxHeight:1080,imageOrientation:!1,imageCrop:!1,disableImageResize:!0,previewMaxWidth:80,previewMaxHeight:80,previewOrientation:!0,previewThumbnail:!0,previewCrop:!1,previewCanvas:!0},processActions:{loadImage:function(a,t){if(t.disabled)return a;var n=this,s=a.files[a.index],r=e.Deferred();return"number"===e.type(t.maxFileSize)&&s.size>t.maxFileSize||t.fileTypes&&!t.fileTypes.test(s.type)||!i(s,(function(e){e.src&&(a.img=e),r.resolveWith(n,[a])}),t)?a:r.promise()},resizeImage:function(a,t){if(t.disabled||!a.canvas&&!a.img)return a;t=e.extend({canvas:!0},t);var n,s=this,r=e.Deferred(),d=t.canvas&&a.canvas||a.img,o=function(e){e&&(e.width!==d.width||e.height!==d.height||t.forceResize)&&(a[e.getContext?"canvas":"img"]=e),a.preview=e,r.resolveWith(s,[a])};if(a.exif){if(!0===t.orientation&&(t.orientation=a.exif.get("Orientation")),t.thumbnail&&(n=a.exif.get("Thumbnail")))return i(n,o,t),r.promise();a.orientation?delete t.orientation:a.orientation=t.orientation}return d?(o(i.scale(d,t)),r.promise()):a},saveImage:function(i,a){if(!i.canvas||a.disabled)return i;var t=this,n=i.files[i.index],s=e.Deferred();return i.canvas.toBlob?(i.canvas.toBlob((function(e){e.name||(n.type===e.type?e.name=n.name:n.name&&(e.name=n.name.replace(/\..+$/,"."+e.type.substr(6)))),n.type!==e.type&&delete i.imageHead,i.files[i.index]=e,s.resolveWith(t,[i])}),a.type||n.type,a.quality),s.promise()):i},loadImageMetaData:function(a,t){if(t.disabled)return a;var n=this,s=e.Deferred();return i.parseMetaData(a.files[a.index],(function(i){e.extend(a,i),s.resolveWith(n,[a])}),t),s.promise()},saveImageMetaData:function(e,i){if(!(e.imageHead&&e.canvas&&e.canvas.toBlob)||i.disabled)return e;var a=e.files[e.index],t=new Blob([e.imageHead,this._blobSlice.call(a,20)],{type:a.type});return t.name=a.name,e.files[e.index]=t,e},setImage:function(e,i){return e.preview&&!i.disabled&&(e.files[e.index][i.name||"preview"]=e.preview),e},deleteImageReferences:function(e,i){return i.disabled||(delete e.img,delete e.canvas,delete e.preview,delete e.imageHead),e}}})}));
@@ -0,0 +1,18 @@
1
+ from django.apps import apps
2
+ from django_tasks import task
3
+
4
+
5
+ @task()
6
+ def set_image_focal_point_task(app_label, model_name, pk):
7
+ model = apps.get_model(app_label, model_name)
8
+ instance = model.objects.get(pk=pk)
9
+ instance.set_focal_point(instance.get_suggested_focal_point())
10
+
11
+ instance.save(
12
+ update_fields=[
13
+ "focal_point_x",
14
+ "focal_point_y",
15
+ "focal_point_width",
16
+ "focal_point_height",
17
+ ]
18
+ )
@@ -63,9 +63,7 @@
63
63
 
64
64
  <dt>{% trans "Usage" %}</dt>
65
65
  <dd>
66
- {% with image.get_usage.count as usage_count_val %}
67
- <a href="{{ image.usage_url }}">{% blocktrans trimmed with usage_count=usage_count_val|intcomma count usage_count_val=usage_count_val %}Used {{ usage_count }} time{% plural %}Used {{ usage_count }} times{% endblocktrans %}</a>
68
- {% endwith %}
66
+ <a href="{{ image.usage_url }}">{% blocktrans trimmed with usage_count=usage_count_val|intcomma count usage_count_val=usage_count_val %}Used {{ usage_count }} time{% plural %}Used {{ usage_count }} times{% endblocktrans %}</a>
69
67
  </dd>
70
68
  </dl>
71
69
  </div>
@@ -11,7 +11,7 @@
11
11
 
12
12
  {% block main_content %}
13
13
  <div class="w-image-url-generator w-mt-8" data-generator-url="{% url 'wagtailimages:generate_url' object.id '__filterspec__' %}">
14
- <form class="w-image-url-generator__form w-mb-5">
14
+ <form class="w-image-url-generator__form w-mb-5" data-controller="w-rules" data-action="change->w-rules#resolve keyup->w-rules#resolve">
15
15
  {% include "wagtailadmin/shared/field.html" with field=form.filter_method %}
16
16
  {% field_row max_content=True %}
17
17
  {% include "wagtailadmin/shared/field.html" with field=form.width %}
@@ -10,7 +10,13 @@
10
10
  {% endblock %}
11
11
 
12
12
  {% block main_content %}
13
- <div class="drop-zone w-mt-8">
13
+ <div
14
+ class="drop-zone w-mt-8"
15
+ data-controller="w-zone"
16
+ data-action="dragover@document->w-zone#noop:prevent drop@document->w-zone#noop:prevent dragover->w-zone#activate drop->w-zone#deactivate dragleave->w-zone#deactivate dragend->w-zone#deactivate"
17
+ data-w-zone-active-class="hovered"
18
+ data-w-zone-delay-value="10"
19
+ >
14
20
  <p>{% trans "Drag and drop images into this area to upload immediately." %}</p>
15
21
  <p>{{ help_text }}</p>
16
22
 
@@ -89,7 +95,6 @@
89
95
 
90
96
  <!-- this exact order of plugins is vital -->
91
97
  <script src="{% versioned_static 'wagtailimages/js/vendor/load-image.min.js' %}"></script>
92
- <script src="{% versioned_static 'wagtailimages/js/vendor/canvas-to-blob.min.js' %}"></script>
93
98
  <script src="{% versioned_static 'wagtailadmin/js/vendor/jquery.iframe-transport.js' %}"></script>
94
99
  <script src="{% versioned_static 'wagtailadmin/js/vendor/jquery.fileupload.js' %}"></script>
95
100
  <script src="{% versioned_static 'wagtailadmin/js/vendor/jquery.fileupload-process.js' %}"></script>
@@ -3,5 +3,5 @@
3
3
 
4
4
  {% block chosen_icon %}
5
5
  {# Empty alt because the chosen item’s title is already displayed next to the image. #}
6
- <img class="chooser__image show-transparency" data-chooser-image alt="" decoding="async" height="{{ preview.height|unlocalize }}" src="{{ preview.url }}" width="{{ preview.width|unlocalize }}">
6
+ <img class="chooser__image show-transparency" data-chooser-image alt="" data-default-alt-text="{{ default_alt_text }}" decoding="async" height="{{ preview.height|unlocalize }}" src="{{ preview.url }}" width="{{ preview.width|unlocalize }}">
7
7
  {% endblock chosen_icon %}
@@ -1453,7 +1453,8 @@ class TestImageDeleteView(WagtailTestUtils, TestCase):
1453
1453
  self.assertEqual(response.status_code, 302)
1454
1454
 
1455
1455
  def test_delete_get_with_protected_reference(self):
1456
- VariousOnDeleteModel.objects.create(protected_image=self.image)
1456
+ with self.captureOnCommitCallbacks(execute=True):
1457
+ VariousOnDeleteModel.objects.create(protected_image=self.image)
1457
1458
  response = self.get()
1458
1459
  self.assertEqual(response.status_code, 200)
1459
1460
  self.assertTemplateUsed(response, "wagtailimages/images/confirm_delete.html")
@@ -1476,7 +1477,8 @@ class TestImageDeleteView(WagtailTestUtils, TestCase):
1476
1477
  )
1477
1478
 
1478
1479
  def test_delete_post_with_protected_reference(self):
1479
- VariousOnDeleteModel.objects.create(protected_image=self.image)
1480
+ with self.captureOnCommitCallbacks(execute=True):
1481
+ VariousOnDeleteModel.objects.create(protected_image=self.image)
1480
1482
  response = self.post()
1481
1483
  self.assertRedirects(response, reverse("wagtailadmin_home"))
1482
1484
  self.assertTrue(Image.objects.filter(id=self.image.id).exists())
@@ -1493,18 +1495,19 @@ class TestUsage(WagtailTestUtils, TestCase):
1493
1495
  )
1494
1496
 
1495
1497
  def test_usage_page(self):
1496
- home_page = Page.objects.get(id=2)
1497
- home_page.add_child(
1498
- instance=EventPage(
1499
- title="Christmas",
1500
- slug="christmas",
1501
- feed_image=self.image,
1502
- date_from=datetime.date.today(),
1503
- audience="private",
1504
- location="Test",
1505
- cost="Test",
1506
- )
1507
- ).save_revision().publish()
1498
+ with self.captureOnCommitCallbacks(execute=True):
1499
+ home_page = Page.objects.get(id=2)
1500
+ home_page.add_child(
1501
+ instance=EventPage(
1502
+ title="Christmas",
1503
+ slug="christmas",
1504
+ feed_image=self.image,
1505
+ date_from=datetime.date.today(),
1506
+ audience="private",
1507
+ location="Test",
1508
+ cost="Test",
1509
+ )
1510
+ ).save_revision().publish()
1508
1511
 
1509
1512
  response = self.client.get(
1510
1513
  reverse("wagtailimages:image_usage", args=[self.image.id])
@@ -1521,9 +1524,10 @@ class TestUsage(WagtailTestUtils, TestCase):
1521
1524
  self.assertNotContains(response, '<table class="listing">')
1522
1525
 
1523
1526
  def test_usage_no_tags(self):
1524
- # tags should not count towards an image's references
1525
- self.image.tags.add("illustration")
1526
- self.image.save()
1527
+ with self.captureOnCommitCallbacks(execute=True):
1528
+ # tags should not count towards an image's references
1529
+ self.image.tags.add("illustration")
1530
+ self.image.save()
1527
1531
  response = self.client.get(
1528
1532
  reverse("wagtailimages:image_usage", args=[self.image.id])
1529
1533
  )
@@ -1531,18 +1535,19 @@ class TestUsage(WagtailTestUtils, TestCase):
1531
1535
  self.assertNotContains(response, '<table class="listing">')
1532
1536
 
1533
1537
  def test_usage_page_with_only_change_permission(self):
1534
- home_page = Page.objects.get(id=2)
1535
- home_page.add_child(
1536
- instance=EventPage(
1537
- title="Christmas",
1538
- slug="christmas",
1539
- feed_image=self.image,
1540
- date_from=datetime.date.today(),
1541
- audience="private",
1542
- location="Test",
1543
- cost="Test",
1544
- )
1545
- ).save_revision().publish()
1538
+ with self.captureOnCommitCallbacks(execute=True):
1539
+ home_page = Page.objects.get(id=2)
1540
+ home_page.add_child(
1541
+ instance=EventPage(
1542
+ title="Christmas",
1543
+ slug="christmas",
1544
+ feed_image=self.image,
1545
+ date_from=datetime.date.today(),
1546
+ audience="private",
1547
+ location="Test",
1548
+ cost="Test",
1549
+ )
1550
+ ).save_revision().publish()
1546
1551
 
1547
1552
  # Create a user with change_image permission but not add_image
1548
1553
  user = self.create_user(
@@ -1871,6 +1876,7 @@ class TestImageChooserChosenView(WagtailTestUtils, TestCase):
1871
1876
  self.image = Image.objects.create(
1872
1877
  title="Test image",
1873
1878
  file=get_test_image_file(),
1879
+ description="Test description",
1874
1880
  )
1875
1881
 
1876
1882
  def get(self, params={}):
@@ -1885,6 +1891,12 @@ class TestImageChooserChosenView(WagtailTestUtils, TestCase):
1885
1891
  response_json = json.loads(response.content.decode())
1886
1892
  self.assertEqual(response_json["step"], "chosen")
1887
1893
  self.assertEqual(response_json["result"]["title"], "Test image")
1894
+ self.assertEqual(
1895
+ set(response_json["result"]["preview"].keys()), {"url", "width", "height"}
1896
+ )
1897
+ self.assertEqual(
1898
+ response_json["result"]["default_alt_text"], "Test description"
1899
+ )
1888
1900
 
1889
1901
  def test_with_multiple_flag(self):
1890
1902
  # if 'multiple' is passed as a URL param, the result should be returned as a single-item list
@@ -1895,6 +1907,13 @@ class TestImageChooserChosenView(WagtailTestUtils, TestCase):
1895
1907
  self.assertEqual(response_json["step"], "chosen")
1896
1908
  self.assertEqual(len(response_json["result"]), 1)
1897
1909
  self.assertEqual(response_json["result"][0]["title"], "Test image")
1910
+ self.assertEqual(
1911
+ set(response_json["result"][0]["preview"].keys()),
1912
+ {"url", "width", "height"},
1913
+ )
1914
+ self.assertEqual(
1915
+ response_json["result"][0]["default_alt_text"], "Test description"
1916
+ )
1898
1917
 
1899
1918
 
1900
1919
  class TestImageChooserChosenMultipleView(WagtailTestUtils, TestCase):
@@ -1905,15 +1924,18 @@ class TestImageChooserChosenMultipleView(WagtailTestUtils, TestCase):
1905
1924
  self.image1 = Image.objects.create(
1906
1925
  title="Test image",
1907
1926
  file=get_test_image_file(),
1927
+ description="Test description",
1908
1928
  )
1909
1929
  self.image2 = Image.objects.create(
1910
1930
  title="Another test image",
1911
1931
  file=get_test_image_file(),
1932
+ description="Another test description",
1912
1933
  )
1913
1934
 
1914
1935
  self.image3 = Image.objects.create(
1915
1936
  title="Unchosen test image",
1916
1937
  file=get_test_image_file(),
1938
+ description="Unchosen test description",
1917
1939
  )
1918
1940
 
1919
1941
  def get(self, params={}):
@@ -1935,6 +1957,8 @@ class TestImageChooserChosenMultipleView(WagtailTestUtils, TestCase):
1935
1957
  self.assertEqual(len(response_json["result"]), 2)
1936
1958
  titles = {item["title"] for item in response_json["result"]}
1937
1959
  self.assertEqual(titles, {"Test image", "Another test image"})
1960
+ alt_texts = {item["default_alt_text"] for item in response_json["result"]}
1961
+ self.assertEqual(alt_texts, {"Test description", "Another test description"})
1938
1962
 
1939
1963
 
1940
1964
  class TestImageChooserSelectFormatView(WagtailTestUtils, TestCase):
@@ -236,7 +236,7 @@ class TestImageBlock(TestImageChooserBlock):
236
236
  value = block.to_python(self.image.id)
237
237
 
238
238
  self.assertEqual(value.id, self.image.id)
239
- self.assertEqual(value.contextual_alt_text, None)
239
+ self.assertEqual(value.contextual_alt_text, "Test image") # Defaulted to title
240
240
  self.assertFalse(value.decorative)
241
241
 
242
242
  def test_to_python_with_dict(self):
@@ -267,8 +267,9 @@ class TestImageBlock(TestImageChooserBlock):
267
267
 
268
268
  def test_bulk_to_python_with_list_of_ints(self):
269
269
  block = ImageBlock(required=False)
270
+ single_image = block.to_python(self.image.id)
270
271
  result = block.bulk_to_python([None, self.image.id, self.image.id])
271
- self.assertEqual(result, [None, self.image, self.image])
272
+ self.assertEqual(result, [None, single_image, single_image])
272
273
 
273
274
  def test_bulk_to_python_with_list_of_dicts(self):
274
275
  block = ImageBlock(required=False)
@@ -984,11 +984,12 @@ class TestUsageCount(TestCase):
984
984
  self.assertEqual(self.image.get_usage().count(), 0)
985
985
 
986
986
  def test_used_image_document_usage_count(self):
987
- page = EventPage.objects.get(id=4)
988
- event_page_carousel_item = EventPageCarouselItem()
989
- event_page_carousel_item.page = page
990
- event_page_carousel_item.image = self.image
991
- event_page_carousel_item.save()
987
+ with self.captureOnCommitCallbacks(execute=True):
988
+ page = EventPage.objects.get(id=4)
989
+ event_page_carousel_item = EventPageCarouselItem()
990
+ event_page_carousel_item.page = page
991
+ event_page_carousel_item.image = self.image
992
+ event_page_carousel_item.save()
992
993
  self.assertEqual(self.image.get_usage().count(), 1)
993
994
 
994
995
 
@@ -1005,11 +1006,12 @@ class TestGetUsage(TestCase):
1005
1006
  self.assertEqual(list(self.image.get_usage()), [])
1006
1007
 
1007
1008
  def test_used_image_document_get_usage(self):
1008
- page = EventPage.objects.get(id=4)
1009
- event_page_carousel_item = EventPageCarouselItem()
1010
- event_page_carousel_item.page = page
1011
- event_page_carousel_item.image = self.image
1012
- event_page_carousel_item.save()
1009
+ with self.captureOnCommitCallbacks(execute=True):
1010
+ page = EventPage.objects.get(id=4)
1011
+ event_page_carousel_item = EventPageCarouselItem()
1012
+ event_page_carousel_item.page = page
1013
+ event_page_carousel_item.image = self.image
1014
+ event_page_carousel_item.save()
1013
1015
 
1014
1016
  self.assertIsInstance(self.image.get_usage()[0], tuple)
1015
1017
  self.assertIsInstance(self.image.get_usage()[0][0], Page)
@@ -366,6 +366,8 @@ class TestFrontendServeView(TestCase):
366
366
  self.assertEqual(response.status_code, 200)
367
367
  self.assertTrue(response.streaming)
368
368
  self.assertEqual(response["Content-Type"], "image/png")
369
+ self.assertEqual(response["Content-Security-Policy"], "default-src 'none'")
370
+ self.assertEqual(response["X-Content-Type-Options"], "nosniff")
369
371
  # Ensure the file can actually be read
370
372
  image = willow.Image.open(b"".join(response.streaming_content))
371
373
  self.assertIsInstance(image, PNGImageFile)
@@ -385,6 +387,8 @@ class TestFrontendServeView(TestCase):
385
387
  self.assertEqual(response.status_code, 200)
386
388
  self.assertTrue(response.streaming)
387
389
  self.assertEqual(response["Content-Type"], "image/svg+xml")
390
+ self.assertEqual(response["Content-Security-Policy"], "default-src 'none'")
391
+ self.assertEqual(response["X-Content-Type-Options"], "nosniff")
388
392
  # Ensure the file can actually be read
389
393
  image = willow.Image.open(BytesIO(b"".join(response.streaming_content)))
390
394
  self.assertIsInstance(image, SvgImageFile)
@@ -462,6 +466,8 @@ class TestFrontendServeView(TestCase):
462
466
  self.assertEqual(response.status_code, 200)
463
467
  self.assertTrue(response.streaming)
464
468
  self.assertEqual(response["Content-Type"], "image/png")
469
+ self.assertEqual(response["Content-Security-Policy"], "default-src 'none'")
470
+ self.assertEqual(response["X-Content-Type-Options"], "nosniff")
465
471
  # Ensure the file can actually be read
466
472
  image = willow.Image.open(b"".join(response.streaming_content))
467
473
  self.assertIsInstance(image, PNGImageFile)
@@ -627,6 +633,8 @@ class TestFrontendSendfileView(TestCase):
627
633
 
628
634
  self.assertEqual(response.status_code, 200)
629
635
  self.assertEqual(response["Content-Type"], "image/png")
636
+ self.assertEqual(response["Content-Security-Policy"], "default-src 'none'")
637
+ self.assertEqual(response["X-Content-Type-Options"], "nosniff")
630
638
 
631
639
  @override_settings(SENDFILE_BACKEND="sendfile.backends.development")
632
640
  def test_sendfile_dummy_backend(self):
@@ -640,6 +648,8 @@ class TestFrontendSendfileView(TestCase):
640
648
 
641
649
  self.assertEqual(response.status_code, 200)
642
650
  self.assertTrue(response.content, msg="Dummy backend response")
651
+ self.assertEqual(response["Content-Security-Policy"], "default-src 'none'")
652
+ self.assertEqual(response["X-Content-Type-Options"], "nosniff")
643
653
 
644
654
 
645
655
  class TestRect(TestCase):
@@ -43,6 +43,7 @@ class ImageChosenResponseMixin(ChosenResponseMixin):
43
43
  "width": preview_image.width,
44
44
  "height": preview_image.height,
45
45
  }
46
+ response_data["default_alt_text"] = image.default_alt_text
46
47
  return response_data
47
48
 
48
49
 
@@ -134,7 +134,6 @@ class EditView(generic.EditView):
134
134
  url_generator_url_name = "wagtailimages:url_generator"
135
135
  header_icon = "image"
136
136
  context_object_name = "image"
137
- _show_breadcrumbs = True
138
137
 
139
138
  @cached_property
140
139
  def model(self):
@@ -186,6 +185,7 @@ class EditView(generic.EditView):
186
185
  def get_context_data(self, **kwargs):
187
186
  context = super().get_context_data(**kwargs)
188
187
  context["next"] = self.next_url
188
+ context["usage_count_val"] = self.object.get_usage().count()
189
189
 
190
190
  try:
191
191
  context["filesize"] = self.object.get_file_size()
@@ -212,7 +212,6 @@ class URLGeneratorView(generic.InspectView):
212
212
  template_name = "wagtailimages/images/url_generator.html"
213
213
  index_url_name = "wagtailimages:index"
214
214
  edit_url_name = "wagtailimages:edit"
215
- _show_breadcrumbs = True
216
215
 
217
216
  def get_page_subtitle(self):
218
217
  return self.object.title
@@ -346,7 +345,6 @@ class CreateView(generic.CreateView):
346
345
  error_message = gettext_lazy("The image could not be created due to errors.")
347
346
  template_name = "wagtailimages/images/add.html"
348
347
  header_icon = "image"
349
- _show_breadcrumbs = True
350
348
 
351
349
  @cached_property
352
350
  def model(self):
@@ -27,7 +27,6 @@ class AddView(WagtailAdminTemplateMixin, BaseAddView):
27
27
  template_name = "wagtailimages/multiple/add.html"
28
28
  header_icon = "image"
29
29
  page_title = gettext_lazy("Add images")
30
- _show_breadcrumbs = True
31
30
 
32
31
  index_url_name = "wagtailimages:index"
33
32
  edit_object_url_name = "wagtailimages:edit_multiple"
@@ -66,7 +66,15 @@ class ServeView(View):
66
66
 
67
67
  # Serve the file
68
68
  rendition.file.open("rb")
69
- return FileResponse(rendition.file, content_type=mime_type)
69
+ response = FileResponse(rendition.file, content_type=mime_type)
70
+
71
+ # Add a CSP header to prevent inline execution
72
+ response["Content-Security-Policy"] = "default-src 'none'"
73
+
74
+ # Prevent browsers from auto-detecting the content-type of a document
75
+ response["X-Content-Type-Options"] = "nosniff"
76
+
77
+ return response
70
78
 
71
79
  def redirect(self, rendition):
72
80
  # Redirect to the file's public location
@@ -80,4 +88,12 @@ class SendFileView(ServeView):
80
88
  backend = None
81
89
 
82
90
  def serve(self, rendition):
83
- return sendfile(self.request, rendition.file.path, backend=self.backend)
91
+ response = sendfile(self.request, rendition.file.path, backend=self.backend)
92
+
93
+ # Add a CSP header to prevent inline execution
94
+ response["Content-Security-Policy"] = "default-src 'none'"
95
+
96
+ # Prevent browsers from auto-detecting the content-type of a document
97
+ response["X-Content-Type-Options"] = "nosniff"
98
+
99
+ return response
wagtail/images/widgets.py CHANGED
@@ -31,11 +31,13 @@ class AdminImageChooser(BaseChooser):
31
31
  "width": preview_image.width,
32
32
  "height": preview_image.height,
33
33
  }
34
+ data["default_alt_text"] = instance.default_alt_text
34
35
  return data
35
36
 
36
37
  def get_context(self, name, value_data, attrs):
37
38
  context = super().get_context(name, value_data, attrs)
38
39
  context["preview"] = value_data.get("preview", {})
40
+ context["default_alt_text"] = value_data.get("default_alt_text", "")
39
41
  return context
40
42
 
41
43
  @property
@@ -44,6 +46,7 @@ class AdminImageChooser(BaseChooser):
44
46
  js=[
45
47
  versioned_static("wagtailimages/js/image-chooser-modal.js"),
46
48
  versioned_static("wagtailimages/js/image-chooser.js"),
49
+ versioned_static("wagtailimages/js/image-chooser-telepath.js"),
47
50
  ]
48
51
  )
49
52