wagtail 7.1.1__py3-none-any.whl → 7.2rc1__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 (345) hide show
  1. wagtail/__init__.py +1 -1
  2. wagtail/actions/copy_page.py +1 -1
  3. wagtail/actions/create_alias.py +1 -1
  4. wagtail/actions/delete_page.py +1 -1
  5. wagtail/actions/publish_page_revision.py +1 -1
  6. wagtail/actions/publish_revision.py +1 -1
  7. wagtail/actions/revert_to_page_revision.py +1 -1
  8. wagtail/actions/unpublish.py +1 -1
  9. wagtail/actions/unpublish_page.py +1 -1
  10. wagtail/admin/auth.py +3 -1
  11. wagtail/admin/checks.py +2 -2
  12. wagtail/admin/filters.py +28 -1
  13. wagtail/admin/forms/collections.py +1 -1
  14. wagtail/admin/forms/comments.py +1 -1
  15. wagtail/admin/forms/models.py +1 -1
  16. wagtail/admin/forms/pages.py +1 -1
  17. wagtail/admin/forms/tags.py +1 -1
  18. wagtail/admin/locale/cs/LC_MESSAGES/django.mo +0 -0
  19. wagtail/admin/locale/cs/LC_MESSAGES/django.po +25 -1
  20. wagtail/admin/locale/en/LC_MESSAGES/django.po +278 -192
  21. wagtail/admin/locale/en/LC_MESSAGES/djangojs.po +29 -15
  22. wagtail/admin/locale/it/LC_MESSAGES/django.mo +0 -0
  23. wagtail/admin/locale/it/LC_MESSAGES/django.po +3 -2
  24. wagtail/admin/locale/nl/LC_MESSAGES/django.mo +0 -0
  25. wagtail/admin/locale/nl/LC_MESSAGES/django.po +57 -3
  26. wagtail/admin/locale/nl/LC_MESSAGES/djangojs.mo +0 -0
  27. wagtail/admin/locale/nl/LC_MESSAGES/djangojs.po +8 -2
  28. wagtail/admin/locale/ru/LC_MESSAGES/django.mo +0 -0
  29. wagtail/admin/locale/ru/LC_MESSAGES/django.po +58 -1
  30. wagtail/admin/locale/tr/LC_MESSAGES/django.mo +0 -0
  31. wagtail/admin/locale/tr/LC_MESSAGES/django.po +3 -2
  32. wagtail/admin/static/wagtailadmin/css/core.css +1 -1
  33. wagtail/admin/static/wagtailadmin/js/bulk-actions.js +1 -1
  34. wagtail/admin/static/wagtailadmin/js/chooser-modal.js +1 -1
  35. wagtail/admin/static/wagtailadmin/js/chooser-widget-telepath.js +1 -1
  36. wagtail/admin/static/wagtailadmin/js/chooser-widget.js +1 -1
  37. wagtail/admin/static/wagtailadmin/js/comments.js +1 -1
  38. wagtail/admin/static/wagtailadmin/js/core.js +1 -1
  39. wagtail/admin/static/wagtailadmin/js/core.js.LICENSE.txt +2 -2
  40. wagtail/admin/static/wagtailadmin/js/date-time-chooser.js +1 -1
  41. wagtail/admin/static/wagtailadmin/js/draftail.js +1 -1
  42. wagtail/admin/static/wagtailadmin/js/filtered-select.js +1 -1
  43. wagtail/admin/static/wagtailadmin/js/icons.js +1 -1
  44. wagtail/admin/static/wagtailadmin/js/modal-workflow.js +1 -1
  45. wagtail/admin/static/wagtailadmin/js/page-chooser-modal.js +1 -1
  46. wagtail/admin/static/wagtailadmin/js/page-chooser-telepath.js +1 -1
  47. wagtail/admin/static/wagtailadmin/js/page-chooser.js +1 -1
  48. wagtail/admin/static/wagtailadmin/js/privacy-switch.js +1 -1
  49. wagtail/admin/static/wagtailadmin/js/sidebar.js +1 -1
  50. wagtail/admin/static/wagtailadmin/js/task-chooser-modal.js +1 -1
  51. wagtail/admin/static/wagtailadmin/js/task-chooser.js +1 -1
  52. wagtail/admin/static/wagtailadmin/js/telepath/blocks.js +1 -1
  53. wagtail/admin/static/wagtailadmin/js/telepath/telepath.js +1 -1
  54. wagtail/admin/static/wagtailadmin/js/telepath/widgets.js +1 -1
  55. wagtail/admin/static/wagtailadmin/js/userbar.js +1 -1
  56. wagtail/admin/static/wagtailadmin/js/userbar.js.LICENSE.txt +2 -2
  57. wagtail/admin/static/wagtailadmin/js/vendor/bootstrap-modal.js +1 -1
  58. wagtail/admin/static/wagtailadmin/js/vendor/bootstrap-transition.js +1 -1
  59. wagtail/admin/static/wagtailadmin/js/vendor/jquery-3.6.0.min.js +1 -1
  60. wagtail/admin/static/wagtailadmin/js/vendor/jquery-ui-1.13.2.min.js +1 -1
  61. wagtail/admin/static/wagtailadmin/js/vendor/jquery.datetimepicker.js +1 -1
  62. wagtail/admin/static/wagtailadmin/js/vendor/jquery.fileupload-process.js +1 -1
  63. wagtail/admin/static/wagtailadmin/js/vendor/jquery.fileupload.js +1 -1
  64. wagtail/admin/static/wagtailadmin/js/vendor/jquery.iframe-transport.js +1 -1
  65. wagtail/admin/static/wagtailadmin/js/vendor/tag-it.js +1 -1
  66. wagtail/admin/static/wagtailadmin/js/vendor.js +1 -1
  67. wagtail/admin/static/wagtailadmin/js/vendor.js.LICENSE.txt +1 -1
  68. wagtail/admin/static/wagtailadmin/js/wagtailadmin.js +1 -1
  69. wagtail/admin/static/wagtailadmin/js/workflow-action.js +1 -1
  70. wagtail/admin/templates/wagtailadmin/account/account.html +2 -0
  71. wagtail/admin/templates/wagtailadmin/base.html +14 -0
  72. wagtail/admin/templates/wagtailadmin/generic/chooser/chooser.html +2 -1
  73. wagtail/admin/templates/wagtailadmin/generic/chooser/creation_form.html +2 -1
  74. wagtail/admin/templates/wagtailadmin/generic/form.html +3 -1
  75. wagtail/admin/templates/wagtailadmin/panels/multi_field_panel_child.html +1 -1
  76. wagtail/admin/templates/wagtailadmin/panels/object_list.html +1 -1
  77. wagtail/admin/templates/wagtailadmin/panels/tabbed_interface.html +3 -2
  78. wagtail/admin/templates/wagtailadmin/shared/formatted_field.html +1 -1
  79. wagtail/admin/templates/wagtailadmin/shared/forms/single_checkbox.html +1 -1
  80. wagtail/admin/templates/wagtailadmin/shared/keyboard_shortcuts_dialog.html +19 -0
  81. wagtail/admin/templates/wagtailadmin/shared/panel.html +1 -1
  82. wagtail/admin/templates/wagtailadmin/shared/set_privacy.html +15 -0
  83. wagtail/admin/templates/wagtailadmin/shared/side_panels/checks.html +28 -1
  84. wagtail/admin/templates/wagtailadmin/shared/workflow_history/detail.html +2 -2
  85. wagtail/admin/templates/wagtailadmin/{pages/listing/_ordering_header.html → tables/ordering_header.html} +2 -2
  86. wagtail/admin/templates/wagtailadmin/tables/title_cell.html +1 -1
  87. wagtail/admin/templates/wagtailadmin/userbar/base.html +6 -3
  88. wagtail/admin/templates/wagtailadmin/userbar/item_admin.html +2 -2
  89. wagtail/admin/templates/wagtailadmin/userbar/item_page_add.html +2 -2
  90. wagtail/admin/templates/wagtailadmin/userbar/item_page_edit.html +2 -2
  91. wagtail/admin/templates/wagtailadmin/userbar/item_page_explore.html +2 -2
  92. wagtail/admin/templates/wagtailadmin/widgets/{daterange_input.html → range_input.html} +1 -1
  93. wagtail/admin/templates/wagtailadmin/workflows/task_chooser/chooser.html +4 -2
  94. wagtail/admin/templatetags/wagtailadmin_tags.py +56 -22
  95. wagtail/admin/tests/api/test_pages.py +7 -7
  96. wagtail/admin/tests/api/test_renderer_classes.py +16 -0
  97. wagtail/admin/tests/pages/test_create_page.py +34 -2
  98. wagtail/admin/tests/pages/test_edit_page.py +128 -14
  99. wagtail/admin/tests/pages/test_explorer_view.py +34 -7
  100. wagtail/admin/tests/pages/test_reorder_page.py +11 -0
  101. wagtail/admin/tests/test_collections_views.py +12 -0
  102. wagtail/admin/tests/test_edit_handlers.py +3 -3
  103. wagtail/admin/tests/test_filters.py +2 -2
  104. wagtail/admin/tests/test_keyboard_shortcuts.py +52 -2
  105. wagtail/admin/tests/test_menu.py +0 -2
  106. wagtail/admin/tests/test_privacy.py +16 -16
  107. wagtail/admin/tests/test_templatetags.py +137 -0
  108. wagtail/admin/tests/test_userbar.py +75 -35
  109. wagtail/admin/tests/test_views_generic.py +34 -0
  110. wagtail/admin/tests/test_workflows.py +34 -0
  111. wagtail/admin/tests/viewsets/test_model_viewset.py +322 -0
  112. wagtail/admin/ui/tables/orderable.py +73 -0
  113. wagtail/admin/ui/tables/pages.py +3 -13
  114. wagtail/admin/userbar.py +6 -1
  115. wagtail/admin/views/collection_privacy.py +6 -2
  116. wagtail/admin/views/generic/__init__.py +1 -0
  117. wagtail/admin/views/generic/mixins.py +20 -2
  118. wagtail/admin/views/generic/models.py +67 -1
  119. wagtail/admin/views/generic/ordering.py +79 -0
  120. wagtail/admin/views/home.py +3 -3
  121. wagtail/admin/views/page_privacy.py +5 -2
  122. wagtail/admin/views/pages/create.py +1 -1
  123. wagtail/admin/views/pages/edit.py +2 -2
  124. wagtail/admin/views/pages/listing.py +7 -42
  125. wagtail/admin/views/pages/move.py +1 -1
  126. wagtail/admin/views/pages/ordering.py +1 -1
  127. wagtail/admin/viewsets/base.py +1 -1
  128. wagtail/admin/viewsets/model.py +49 -1
  129. wagtail/admin/wagtail_hooks.py +2 -1
  130. wagtail/admin/widgets/slug.py +10 -10
  131. wagtail/api/v2/serializers.py +1 -1
  132. wagtail/api/v2/tests/test_renderer_classes.py +32 -0
  133. wagtail/apps.py +2 -0
  134. wagtail/bin/wagtail.py +1 -1
  135. wagtail/blocks/struct_block.py +2 -1
  136. wagtail/contrib/forms/locale/en/LC_MESSAGES/django.po +14 -14
  137. wagtail/contrib/forms/locale/nl/LC_MESSAGES/django.mo +0 -0
  138. wagtail/contrib/forms/locale/nl/LC_MESSAGES/django.po +19 -2
  139. wagtail/contrib/forms/locale/ru/LC_MESSAGES/django.mo +0 -0
  140. wagtail/contrib/forms/locale/ru/LC_MESSAGES/django.po +18 -1
  141. wagtail/contrib/frontend_cache/tests.py +4 -2
  142. wagtail/contrib/redirects/locale/en/LC_MESSAGES/django.po +4 -4
  143. wagtail/contrib/redirects/tests/test_tmp_storages.py +20 -0
  144. wagtail/contrib/redirects/tmp_storages.py +1 -1
  145. wagtail/contrib/redirects/views.py +3 -3
  146. wagtail/contrib/search_promotions/locale/en/LC_MESSAGES/django.po +3 -3
  147. wagtail/contrib/search_promotions/locale/tr/LC_MESSAGES/django.mo +0 -0
  148. wagtail/contrib/search_promotions/locale/tr/LC_MESSAGES/django.po +43 -3
  149. wagtail/contrib/search_promotions/static/wagtailsearchpromotions/js/query-chooser-modal.js +1 -1
  150. wagtail/contrib/search_promotions/views/settings.py +2 -2
  151. wagtail/contrib/settings/locale/cs/LC_MESSAGES/django.mo +0 -0
  152. wagtail/contrib/settings/locale/cs/LC_MESSAGES/django.po +6 -1
  153. wagtail/contrib/settings/locale/en/LC_MESSAGES/django.po +1 -1
  154. wagtail/contrib/settings/locale/nl/LC_MESSAGES/django.mo +0 -0
  155. wagtail/contrib/settings/locale/nl/LC_MESSAGES/django.po +6 -2
  156. wagtail/contrib/settings/locale/ru/LC_MESSAGES/django.mo +0 -0
  157. wagtail/contrib/settings/locale/ru/LC_MESSAGES/django.po +6 -1
  158. wagtail/contrib/settings/tests/site_specific/test_admin.py +40 -6
  159. wagtail/contrib/simple_translation/locale/en/LC_MESSAGES/django.po +1 -1
  160. wagtail/contrib/styleguide/locale/en/LC_MESSAGES/django.po +1 -1
  161. wagtail/contrib/styleguide/templates/wagtailstyleguide/base.html +5 -5
  162. wagtail/contrib/table_block/blocks.py +1 -0
  163. wagtail/contrib/table_block/locale/en/LC_MESSAGES/django.po +5 -1
  164. wagtail/contrib/table_block/static/table_block/js/table.js +1 -1
  165. wagtail/contrib/typed_table_block/locale/en/LC_MESSAGES/django.po +1 -1
  166. wagtail/contrib/typed_table_block/static/typed_table_block/js/typed_table_block.js +1 -1
  167. wagtail/coreutils.py +5 -5
  168. wagtail/documents/forms.py +18 -1
  169. wagtail/documents/locale/en/LC_MESSAGES/django.po +10 -10
  170. wagtail/documents/locale/nl/LC_MESSAGES/django.mo +0 -0
  171. wagtail/documents/locale/nl/LC_MESSAGES/django.po +9 -0
  172. wagtail/documents/locale/ru/LC_MESSAGES/django.mo +0 -0
  173. wagtail/documents/locale/ru/LC_MESSAGES/django.po +9 -0
  174. wagtail/documents/models.py +1 -1
  175. wagtail/documents/static/wagtaildocs/js/add-multiple.js +1 -1
  176. wagtail/documents/static/wagtaildocs/js/document-chooser-modal.js +1 -1
  177. wagtail/documents/static/wagtaildocs/js/document-chooser-telepath.js +1 -1
  178. wagtail/documents/static/wagtaildocs/js/document-chooser.js +1 -1
  179. wagtail/documents/templates/wagtaildocs/documents/add.html +0 -34
  180. wagtail/documents/tests/test_admin_views.py +132 -26
  181. wagtail/documents/tests/test_collection_privacy.py +18 -4
  182. wagtail/documents/tests/test_form_overrides.py +1 -1
  183. wagtail/documents/tests/test_search.py +21 -8
  184. wagtail/documents/views/documents.py +1 -1
  185. wagtail/embeds/locale/en/LC_MESSAGES/django.po +1 -1
  186. wagtail/embeds/static/wagtailembeds/js/embed-chooser-modal.js +1 -1
  187. wagtail/images/forms.py +16 -1
  188. wagtail/images/locale/cs/LC_MESSAGES/django.mo +0 -0
  189. wagtail/images/locale/cs/LC_MESSAGES/django.po +12 -1
  190. wagtail/images/locale/en/LC_MESSAGES/django.po +57 -46
  191. wagtail/images/locale/nl/LC_MESSAGES/django.mo +0 -0
  192. wagtail/images/locale/nl/LC_MESSAGES/django.po +37 -14
  193. wagtail/images/locale/ru/LC_MESSAGES/django.mo +0 -0
  194. wagtail/images/locale/ru/LC_MESSAGES/django.po +20 -1
  195. wagtail/images/models.py +1 -1
  196. wagtail/images/static/wagtailimages/js/add-multiple.js +1 -1
  197. wagtail/images/static/wagtailimages/js/focal-point-chooser.js +1 -1
  198. wagtail/images/static/wagtailimages/js/image-block.js +1 -1
  199. wagtail/images/static/wagtailimages/js/image-chooser-modal.js +1 -1
  200. wagtail/images/static/wagtailimages/js/image-chooser-telepath.js +1 -1
  201. wagtail/images/static/wagtailimages/js/image-chooser.js +1 -1
  202. wagtail/images/static/wagtailimages/js/image-url-generator.js +1 -1
  203. wagtail/images/static/wagtailimages/js/vendor/jquery.Jcrop.min.js +1 -1
  204. wagtail/images/static/wagtailimages/js/vendor/jquery.fileupload-image.js +1 -1
  205. wagtail/images/static/wagtailimages/js/vendor/jquery.fileupload-validate.js +1 -1
  206. wagtail/images/static/wagtailimages/js/vendor/load-image.min.js +1 -1
  207. wagtail/images/templates/wagtailimages/chooser/chooser.html +22 -13
  208. wagtail/images/templates/wagtailimages/chooser/image_preview_column_cell.html +10 -0
  209. wagtail/images/templates/wagtailimages/chooser/results.html +24 -20
  210. wagtail/images/templates/wagtailimages/chooser/title_column_cell.html +15 -0
  211. wagtail/images/templates/wagtailimages/images/add.html +0 -34
  212. wagtail/images/templates/wagtailimages/images/index.html +3 -3
  213. wagtail/images/templates/wagtailimages/images/index_results.html +1 -1
  214. wagtail/images/templates/wagtailimages/images/layout_toggle_button.html +8 -7
  215. wagtail/images/templatetags/wagtailimages_tags.py +2 -2
  216. wagtail/images/tests/test_admin_views.py +87 -0
  217. wagtail/images/tests/test_form_overrides.py +1 -1
  218. wagtail/images/tests/test_models.py +48 -9
  219. wagtail/images/views/chooser.py +66 -2
  220. wagtail/locale/en/LC_MESSAGES/django.po +55 -55
  221. wagtail/locale/is_IS/LC_MESSAGES/django.mo +0 -0
  222. wagtail/locale/is_IS/LC_MESSAGES/django.po +3 -3
  223. wagtail/locale/nl/LC_MESSAGES/django.mo +0 -0
  224. wagtail/locale/nl/LC_MESSAGES/django.po +11 -2
  225. wagtail/locale/ru/LC_MESSAGES/django.mo +0 -0
  226. wagtail/locale/ru/LC_MESSAGES/django.po +11 -1
  227. wagtail/locales/locale/en/LC_MESSAGES/django.po +1 -1
  228. wagtail/locales/locale/nl/LC_MESSAGES/django.mo +0 -0
  229. wagtail/locales/locale/nl/LC_MESSAGES/django.po +12 -1
  230. wagtail/locales/locale/ru/LC_MESSAGES/django.mo +0 -0
  231. wagtail/locales/locale/ru/LC_MESSAGES/django.po +10 -1
  232. wagtail/locales/views.py +2 -2
  233. wagtail/models/orderable.py +10 -0
  234. wagtail/models/pages.py +9 -11
  235. wagtail/models/sites.py +1 -1
  236. wagtail/models/workflows.py +8 -5
  237. wagtail/project_template/home/tests.py +6 -7
  238. wagtail/project_template/project_name/settings/base.py +9 -9
  239. wagtail/project_template/requirements.txt +1 -1
  240. wagtail/query.py +7 -2
  241. wagtail/rich_text/rewriters.py +1 -1
  242. wagtail/search/apps.py +4 -49
  243. wagtail/search/backends/__init__.py +1 -113
  244. wagtail/search/backends/base.py +1 -547
  245. wagtail/search/backends/database/__init__.py +1 -50
  246. wagtail/search/backends/database/fallback.py +1 -253
  247. wagtail/search/backends/database/mysql/mysql.py +1 -700
  248. wagtail/search/backends/database/mysql/query.py +1 -258
  249. wagtail/search/backends/database/postgres/postgres.py +1 -749
  250. wagtail/search/backends/database/postgres/query.py +1 -83
  251. wagtail/search/backends/database/postgres/weights.py +1 -63
  252. wagtail/search/backends/database/sqlite/query.py +1 -294
  253. wagtail/search/backends/database/sqlite/sqlite.py +1 -719
  254. wagtail/search/backends/database/sqlite/utils.py +1 -35
  255. wagtail/search/backends/deprecation.py +45 -0
  256. wagtail/search/backends/elasticsearch7.py +18 -1260
  257. wagtail/search/backends/elasticsearch8.py +21 -96
  258. wagtail/search/backends/elasticsearch9.py +35 -0
  259. wagtail/search/backends/opensearch2.py +35 -0
  260. wagtail/search/backends/opensearch3.py +35 -0
  261. wagtail/search/index.py +1 -358
  262. wagtail/search/locale/en/LC_MESSAGES/django.po +2 -10
  263. wagtail/search/management/commands/update_index.py +1 -205
  264. wagtail/search/management/commands/wagtail_update_index.py +1 -4
  265. wagtail/search/models.py +32 -158
  266. wagtail/search/query.py +1 -114
  267. wagtail/search/queryset.py +1 -43
  268. wagtail/search/signal_handlers.py +1 -24
  269. wagtail/search/tasks.py +1 -10
  270. wagtail/search/tests/test_elasticsearch.py +22 -0
  271. wagtail/search/utils.py +1 -206
  272. wagtail/sites/locale/en/LC_MESSAGES/django.po +1 -1
  273. wagtail/snippets/locale/en/LC_MESSAGES/django.po +3 -3
  274. wagtail/snippets/locale/ru/LC_MESSAGES/django.mo +0 -0
  275. wagtail/snippets/locale/ru/LC_MESSAGES/django.po +8 -1
  276. wagtail/snippets/locale/tr/LC_MESSAGES/django.mo +0 -0
  277. wagtail/snippets/locale/tr/LC_MESSAGES/django.po +8 -1
  278. wagtail/snippets/static/wagtailsnippets/js/snippet-chooser-telepath.js +1 -1
  279. wagtail/snippets/static/wagtailsnippets/js/snippet-chooser.js +1 -1
  280. wagtail/snippets/tests/test_preview.py +5 -6
  281. wagtail/snippets/tests/test_reordering.py +319 -0
  282. wagtail/snippets/tests/test_snippets.py +65 -12
  283. wagtail/snippets/views/snippets.py +16 -0
  284. wagtail/test/numberformat.py +30 -0
  285. wagtail/test/settings.py +35 -12
  286. wagtail/test/testapp/fields.py +12 -0
  287. wagtail/test/testapp/migrations/0056_commentablejsonpage.py +50 -0
  288. wagtail/test/testapp/migrations/0057_featurecompletetoy_sort_order.py +23 -0
  289. wagtail/test/testapp/migrations/0058_customlocktask.py +31 -0
  290. wagtail/test/testapp/models.py +27 -0
  291. wagtail/test/testapp/urls.py +1 -0
  292. wagtail/test/testapp/views.py +18 -2
  293. wagtail/test/utils/page_tests.py +17 -17
  294. wagtail/test/utils/template_tests.py +4 -6
  295. wagtail/test/utils/wagtail_tests.py +1 -2
  296. wagtail/tests/test_blocks.py +15 -0
  297. wagtail/tests/test_page_model.py +15 -0
  298. wagtail/{search/tests → tests}/test_page_search.py +29 -2
  299. wagtail/tests/test_search_fields.py +69 -0
  300. wagtail/tests/test_tests.py +62 -6
  301. wagtail/tests/test_workflow.py +25 -1
  302. wagtail/users/locale/cs/LC_MESSAGES/django.mo +0 -0
  303. wagtail/users/locale/cs/LC_MESSAGES/django.po +3 -0
  304. wagtail/users/locale/en/LC_MESSAGES/django.po +2 -2
  305. wagtail/users/locale/nl/LC_MESSAGES/django.mo +0 -0
  306. wagtail/users/locale/nl/LC_MESSAGES/django.po +6 -3
  307. wagtail/users/locale/ru/LC_MESSAGES/django.mo +0 -0
  308. wagtail/users/locale/ru/LC_MESSAGES/django.po +5 -1
  309. wagtail/users/locale/tr/LC_MESSAGES/django.mo +0 -0
  310. wagtail/users/locale/tr/LC_MESSAGES/django.po +78 -4
  311. wagtail/users/templates/wagtailusers/users/create.html +2 -0
  312. wagtail/users/templates/wagtailusers/users/edit.html +2 -0
  313. wagtail/users/tests/test_admin_views.py +4 -0
  314. wagtail/users/views/users.py +1 -1
  315. {wagtail-7.1.1.dist-info → wagtail-7.2rc1.dist-info}/METADATA +7 -6
  316. {wagtail-7.1.1.dist-info → wagtail-7.2rc1.dist-info}/RECORD +322 -328
  317. wagtail/admin/templates/wagtailadmin/collection_privacy/set_privacy.html +0 -13
  318. wagtail/admin/templates/wagtailadmin/page_privacy/set_privacy.html +0 -13
  319. wagtail/search/tests/__init__.py +0 -0
  320. wagtail/search/tests/elasticsearch_common_tests.py +0 -251
  321. wagtail/search/tests/test_backends.py +0 -1215
  322. wagtail/search/tests/test_db_backend.py +0 -62
  323. wagtail/search/tests/test_elasticsearch7_backend.py +0 -1452
  324. wagtail/search/tests/test_elasticsearch8_backend.py +0 -15
  325. wagtail/search/tests/test_index_functions.py +0 -256
  326. wagtail/search/tests/test_indexed_class.py +0 -157
  327. wagtail/search/tests/test_mysql_backend.py +0 -192
  328. wagtail/search/tests/test_postgres_backend.py +0 -210
  329. wagtail/search/tests/test_queries.py +0 -332
  330. wagtail/search/tests/test_related_fields.py +0 -102
  331. wagtail/search/tests/test_sqlite_backend.py +0 -52
  332. wagtail/test/search/__init__.py +0 -0
  333. wagtail/test/search/apps.py +0 -9
  334. wagtail/test/search/fixtures/search.json +0 -545
  335. wagtail/test/search/migrations/0001_initial.py +0 -146
  336. wagtail/test/search/migrations/0002_bookunindexed.py +0 -43
  337. wagtail/test/search/migrations/0003_book_summary.py +0 -18
  338. wagtail/test/search/migrations/__init__.py +0 -0
  339. wagtail/test/search/models.py +0 -137
  340. /wagtail/admin/templates/wagtailadmin/{pages/listing/_ordering_cell.html → tables/ordering_cell.html} +0 -0
  341. /wagtail/{search/checks.py → checks.py} +0 -0
  342. {wagtail-7.1.1.dist-info → wagtail-7.2rc1.dist-info}/WHEEL +0 -0
  343. {wagtail-7.1.1.dist-info → wagtail-7.2rc1.dist-info}/entry_points.txt +0 -0
  344. {wagtail-7.1.1.dist-info → wagtail-7.2rc1.dist-info}/licenses/LICENSE +0 -0
  345. {wagtail-7.1.1.dist-info → wagtail-7.2rc1.dist-info}/top_level.txt +0 -0
@@ -4,26 +4,30 @@
4
4
  {% block listing_title %}<h2>{% trans "Latest images" %}</h2>{% endblock %}
5
5
 
6
6
  {% block results_listing %}
7
- <ul class="listing horiz images chooser">
8
- {% for image in results %}
9
- <li>
10
- {% if is_multiple_choice %}
11
- <label title="Select {% if collections %}{{ image.collection.name }} » {% endif %}{{ image.title }}">
12
- <div class="image">{% image image max-165x165 class="show-transparency" %}</div>
13
- <h3>
14
- <input type="checkbox" data-multiple-choice-select name="id" value="{{ image.id }}" title="{% blocktrans trimmed with title=image.title %}Select {{ title }}{% endblocktrans %}">
15
- {{ image.title|ellipsistrim:60 }}
16
- </h3>
17
- </label>
18
- {% else %}
19
- <a data-chooser-modal-choice class="image-choice" title="{% if collections %}{{ image.collection.name }} » {% endif %}{{ image.title }}" href="{{ image.chosen_url }}">
20
- <div class="image">{% image image max-165x165 class="show-transparency" %}</div>
21
- <h3>{{ image.title|ellipsistrim:60 }}</h3>
22
- </a>
23
- {% endif %}
24
- </li>
25
- {% endfor %}
26
- </ul>
7
+ {% if layout == "list" %}
8
+ {% include "wagtailadmin/tables/table.html" with table_classname="listing" %}
9
+ {% else %}
10
+ <ul class="listing horiz images chooser">
11
+ {% for image in results %}
12
+ <li>
13
+ {% if is_multiple_choice %}
14
+ <label title="Select {% if collections %}{{ image.collection.name }} » {% endif %}{{ image.title }}">
15
+ <div class="image">{% image image max-165x165 class="show-transparency" %}</div>
16
+ <h3>
17
+ <input type="checkbox" data-multiple-choice-select name="id" value="{{ image.id }}" title="{% blocktrans trimmed with title=image.title %}Select {{ title }}{% endblocktrans %}">
18
+ {{ image.title|ellipsistrim:60 }}
19
+ </h3>
20
+ </label>
21
+ {% else %}
22
+ <a data-chooser-modal-choice class="image-choice" title="{% if collections %}{{ image.collection.name }} » {% endif %}{{ image.title }}" href="{{ image.chosen_url }}">
23
+ <div class="image">{% image image max-165x165 class="show-transparency" %}</div>
24
+ <h3>{{ image.title|ellipsistrim:60 }}</h3>
25
+ </a>
26
+ {% endif %}
27
+ </li>
28
+ {% endfor %}
29
+ </ul>
30
+ {% endif %}
27
31
  {% endblock %}
28
32
 
29
33
  {% block no_items_message %}
@@ -0,0 +1,15 @@
1
+ {% extends "wagtailadmin/tables/title_cell.html" %}
2
+ {% load wagtailadmin_tags %}
3
+
4
+ {% block title %}
5
+ {% if link_url %}
6
+ <a data-chooser-modal-choice href="{{ link_url }}">{{ value }}</a>
7
+ {% else %}
8
+ {{ value }}
9
+ {% endif %}
10
+ {% endblock %}
11
+ {% block extra %}
12
+ <div class="filename-wrapper">
13
+ <span>{{ instance.filename }}</span>
14
+ </div>
15
+ {% endblock %}
@@ -2,40 +2,6 @@
2
2
  {% load wagtailimages_tags wagtailadmin_tags i18n %}
3
3
  {% block titletag %}{% trans "Add an image" %}{% endblock %}
4
4
 
5
- {% block extra_js %}
6
- {{ block.super }}
7
-
8
- <script>
9
- $(function() {
10
- $('#id_file').on(
11
- 'change',
12
- function() {
13
- var $titleField = $('#id_title');
14
-
15
- // do not override a title that already exists (from manual editing or previous upload)
16
- if ($titleField.val()) return;
17
-
18
- // file widget value example: `C:\fakepath\image.jpg` - convert to just the filename part
19
- var filename = $(this).val().split('\\').slice(-1)[0];
20
- var data = { title: filename.replace(/\.[^.]+$/, '') };
21
- var maxTitleLength = parseInt($titleField.attr('maxLength') || '0', 10) || null;
22
-
23
- // allow an event handler to customise data or call event.preventDefault to stop any title pre-filling
24
- var form = $(this).closest('form').get(0);
25
- var event = form.dispatchEvent(new CustomEvent(
26
- 'wagtail:images-upload',
27
- { bubbles: true, cancelable: true, detail: { data: data, filename: filename, maxTitleLength: maxTitleLength } }
28
- ));
29
-
30
- if (!event) return; // do not set a title if event.preventDefault(); is called by handler
31
-
32
- $titleField.val(data.title);
33
- }
34
- );
35
- });
36
- </script>
37
- {% endblock %}
38
-
39
5
  {% block actions %}
40
6
  <button
41
7
  type="submit"
@@ -8,9 +8,6 @@
8
8
 
9
9
  {% block slim_header %}
10
10
  {% fragment as extra_form_fields %}
11
- <div id="layout-toggle-button">
12
- {% include "wagtailimages/images/layout_toggle_button.html" %}
13
- </div>
14
11
  {% rawformattedfield label_text=_("Sort by") id_for_label="order_images_by" sr_only_label=True %}
15
12
  <select id="order_images_by" name="ordering">
16
13
  {% for ordering, ordering_text in ORDERING_OPTIONS.items %}
@@ -18,6 +15,9 @@
18
15
  {% endfor %}
19
16
  </select>
20
17
  {% endrawformattedfield %}
18
+ <div id="layout-switch-control" class="w-layout-switch-control">
19
+ {% include "wagtailimages/images/layout_toggle_button.html" %}
20
+ </div>
21
21
  {% endfragment %}
22
22
  {% include "wagtailimages/images/image_listing_header.html" with breadcrumbs_items=breadcrumbs_items side_panels=side_panels history_url=history_url title=header_title search_url=index_results_url search_form=search_form filters=filters buttons=header_buttons icon_name=header_icon extra_form_fields=extra_form_fields only %}
23
23
  {% endblock %}
@@ -4,7 +4,7 @@
4
4
 
5
5
  {% block before_results %}
6
6
  {{ block.super }}
7
- <template data-controller="w-teleport" data-w-teleport-target-value="#layout-toggle-button" data-w-teleport-reset-value="true">
7
+ <template data-controller="w-teleport" data-w-teleport-target-value="#layout-switch-control" data-w-teleport-reset-value="true">
8
8
  {% include "wagtailimages/images/layout_toggle_button.html" %}
9
9
  </template>
10
10
  {% endblock %}
@@ -1,10 +1,11 @@
1
1
  {% load wagtailadmin_tags i18n %}
2
2
 
3
- <label class="w-slim-header-action-button w-layout-toggle-button">
4
- {% if layout == "list" %}
5
- {% icon name="grid" %}
6
- {% else %}
7
- {% icon name="list-ul" %}
8
- {% endif %}
9
- <input type="checkbox" aria-label="{% translate 'Toggle layout' %}" name="layout" value="list" {% if layout == "list" %}checked{% endif %} data-controller="w-tooltip" data-w-tooltip-content-value="{% translate 'Toggle layout' %}">
3
+ <label class="w-layout-toggle-button w-layout-toggle-button-right" tabindex="0">
4
+ {% icon name="grid" %}
5
+ <input type="radio" aria-label="{% translate 'Grid layout' %}" name="layout" value="grid" {% if layout == "grid" %}checked{% endif %} data-controller="w-tooltip" data-w-tooltip-content-value="{% translate 'Grid layout' %}">
10
6
  </label>
7
+ <label class="w-layout-toggle-button w-layout-toggle-button-left" tabindex="0">
8
+ {% icon name="list-ul" %}
9
+ <input type="radio" aria-label="{% translate 'List layout' %}" name="layout" value="list" {% if layout == "list" %}checked{% endif %} data-controller="w-tooltip" data-w-tooltip-content-value="{% translate 'List layout' %}">
10
+ </label>
11
+
@@ -104,11 +104,11 @@ class ImageNode(template.Node):
104
104
  image_expr,
105
105
  filter_specs,
106
106
  output_var_name=None,
107
- attrs={},
107
+ attrs=None,
108
108
  ):
109
109
  self.image_expr = image_expr
110
110
  self.output_var_name = output_var_name
111
- self.attrs = attrs
111
+ self.attrs = attrs or {}
112
112
  self.filter_specs = filter_specs
113
113
 
114
114
  def get_filter(self):
@@ -609,6 +609,46 @@ class TestImageIndexView(WagtailTestUtils, TestCase):
609
609
  expected_order,
610
610
  )
611
611
 
612
+ def test_filter_by_usage_count(self):
613
+ unused_image = Image.objects.create(
614
+ title="an abandoned toy",
615
+ file=get_test_image_file(size=(1, 1)),
616
+ )
617
+ with self.captureOnCommitCallbacks(execute=True):
618
+ VariousOnDeleteModel.objects.create(protected_image=self.kitten_image)
619
+ VariousOnDeleteModel.objects.create(protected_image=self.kitten_image)
620
+ VariousOnDeleteModel.objects.create(protected_image=self.puppy_image)
621
+
622
+ response = self.client.get(
623
+ reverse("wagtailimages:index"),
624
+ {"usage_count_min": "1"},
625
+ )
626
+ self.assertEqual(response.status_code, 200)
627
+ self.assertCountEqual(
628
+ response.context["page_obj"].object_list,
629
+ [self.kitten_image, self.puppy_image],
630
+ )
631
+
632
+ response = self.client.get(
633
+ reverse("wagtailimages:index"),
634
+ {"usage_count_min": "1", "usage_count_max": "1"},
635
+ )
636
+ self.assertEqual(response.status_code, 200)
637
+ self.assertCountEqual(
638
+ response.context["page_obj"].object_list,
639
+ [self.puppy_image],
640
+ )
641
+
642
+ response = self.client.get(
643
+ reverse("wagtailimages:index"),
644
+ {"usage_count_max": "0"},
645
+ )
646
+ self.assertEqual(response.status_code, 200)
647
+ self.assertCountEqual(
648
+ response.context["page_obj"].object_list,
649
+ [unused_image],
650
+ )
651
+
612
652
 
613
653
  class TestBulkActionsColumn(WagtailTestUtils, TestCase):
614
654
  def setUp(self):
@@ -857,6 +897,25 @@ class TestImageAddView(AdminTemplateTestUtils, WagtailTestUtils, TestCase):
857
897
  response.content,
858
898
  )
859
899
 
900
+ soup = self.get_soup(response.content)
901
+ form = soup.select_one("main form")
902
+ self.assertIsNotNone(form)
903
+ title_input = form.select_one('input[type="text"][name="title"]')
904
+ self.assertIsNotNone(title_input)
905
+ self.assertEqual(title_input.get("id"), "id_title")
906
+ file_input = form.select_one('input[type="file"][name="file"]')
907
+ self.assertIsNotNone(file_input)
908
+ expected_attributes = {
909
+ "data-controller": "w-sync",
910
+ "data-action": "change->w-sync#apply",
911
+ "data-w-sync-bubbles-param": "true",
912
+ "data-w-sync-name-value": "wagtail:images-upload",
913
+ "data-w-sync-normalize-value": "true",
914
+ "data-w-sync-target-value": "#id_title",
915
+ }
916
+ for attr, expected_value in expected_attributes.items():
917
+ self.assertEqual(file_input.get(attr), expected_value)
918
+
860
919
  def test_get_with_collections(self):
861
920
  root_collection = Collection.get_first_root_node()
862
921
  root_collection.add_child(name="Evil plans")
@@ -1913,6 +1972,27 @@ class TestImageChooserView(WagtailTestUtils, TestCase):
1913
1972
  self.assertEqual(
1914
1973
  soup.select_one('input[type="file"]').get("accept"), "image/*, image/avif"
1915
1974
  )
1975
+ form = soup.select_one("form[data-chooser-modal-creation-form]")
1976
+ self.assertIsNotNone(form)
1977
+ title_input = form.select_one(
1978
+ 'input[type="text"][name="image-chooser-upload-title"]'
1979
+ )
1980
+ self.assertIsNotNone(title_input)
1981
+ self.assertEqual(title_input.get("id"), "id_image-chooser-upload-title")
1982
+ file_input = form.select_one(
1983
+ 'input[type="file"][name="image-chooser-upload-file"]'
1984
+ )
1985
+ self.assertIsNotNone(file_input)
1986
+ expected_attributes = {
1987
+ "data-controller": "w-sync",
1988
+ "data-action": "change->w-sync#apply",
1989
+ "data-w-sync-bubbles-param": "true",
1990
+ "data-w-sync-name-value": "wagtail:images-upload",
1991
+ "data-w-sync-normalize-value": "true",
1992
+ "data-w-sync-target-value": "#id_image-chooser-upload-title",
1993
+ }
1994
+ for attr, expected_value in expected_attributes.items():
1995
+ self.assertEqual(file_input.get(attr), expected_value)
1916
1996
 
1917
1997
  def test_simple_with_collection_nesting(self):
1918
1998
  root_collection = Collection.get_first_root_node()
@@ -3808,6 +3888,13 @@ class TestURLGeneratorView(AdminTemplateTestUtils, WagtailTestUtils, TestCase):
3808
3888
  response.content,
3809
3889
  )
3810
3890
 
3891
+ soup = self.get_soup(response.content)
3892
+ form = soup.select_one("main form")
3893
+ closeness = form.select_one("input[name=closeness]")
3894
+ self.assertIsNotNone(closeness)
3895
+ self.assertEqual(closeness.get("min"), "0")
3896
+ self.assertEqual(closeness.get("max"), "100")
3897
+
3811
3898
  def test_get_bad_permissions(self):
3812
3899
  """
3813
3900
  This tests that the view returns a "permission denied" redirect if a user without correct
@@ -49,7 +49,7 @@ class TestImageFormOverride(TestCase):
49
49
  self.assertIn("tags", form.errors)
50
50
  self.assertEqual(
51
51
  form.errors["tags"][0],
52
- "Tag(s) ['{val}'] are over {max_tag_length} characters".format(
52
+ "Tag(s) ['{val}'] are over {max_tag_length} characters.".format(
53
53
  val=long_value,
54
54
  max_tag_length=taggit_models.TagBase._meta.get_field("name").max_length,
55
55
  ),
@@ -1,9 +1,11 @@
1
1
  import hashlib
2
2
  import unittest
3
+ from io import StringIO
4
+ from unittest import mock
3
5
 
4
6
  from django.conf import settings
5
7
  from django.contrib.auth.models import Group, Permission
6
- from django.core import checks
8
+ from django.core import checks, management
7
9
  from django.core.cache import caches
8
10
  from django.core.files import File
9
11
  from django.core.files.storage import Storage, default_storage, storages
@@ -25,6 +27,10 @@ from wagtail.images.models import (
25
27
  )
26
28
  from wagtail.images.rect import Rect
27
29
  from wagtail.models import Collection, GroupCollectionPermission, Page, ReferenceIndex
30
+ from wagtail.test.dummy_external_storage import (
31
+ DummyExternalStorage,
32
+ DummyExternalStorageFile,
33
+ )
28
34
  from wagtail.test.testapp.models import (
29
35
  CustomRendition,
30
36
  EventPage,
@@ -45,6 +51,16 @@ class CustomStorage(Storage):
45
51
  pass
46
52
 
47
53
 
54
+ class AnotherDummyExternalStorage(DummyExternalStorage):
55
+ def _open(self, name, mode="rb"):
56
+ # External storages has their own File type
57
+ return AnotherDummyExternalStorageFile(open(self.wrapped.path(name), mode))
58
+
59
+
60
+ class AnotherDummyExternalStorageFile(DummyExternalStorageFile):
61
+ pass
62
+
63
+
48
64
  class TestImage(TestCase):
49
65
  def setUp(self):
50
66
  # Create an image for running tests on
@@ -122,6 +138,18 @@ class TestImage(TestCase):
122
138
  def test_is_stored_locally_with_external_storage(self):
123
139
  self.assertFalse(self.image.is_stored_locally())
124
140
 
141
+ @override_settings(
142
+ DEFAULT_FILE_STORAGE="wagtail.test.dummy_external_storage.DummyExternalStorage"
143
+ )
144
+ def test_reopen_based_on_storage_that_is_dynamically_set(self):
145
+ self.image.file.close()
146
+ # Simulate the case is which the storage is set dynamically
147
+ with mock.patch.object(
148
+ self.image.file, "storage", AnotherDummyExternalStorage()
149
+ ):
150
+ with self.image.open_file() as file:
151
+ self.assertIsInstance(file, AnotherDummyExternalStorageFile)
152
+
125
153
  def test_get_file_size(self):
126
154
  file_size = self.image.get_file_size()
127
155
  self.assertIsInstance(file_size, int)
@@ -1262,6 +1290,13 @@ class TestIssue613(WagtailTestUtils, TestCase):
1262
1290
  self.search_backend = self.get_elasticsearch_backend()
1263
1291
  self.login()
1264
1292
 
1293
+ management.call_command(
1294
+ "update_index",
1295
+ backend_name="elasticsearch",
1296
+ stdout=StringIO(),
1297
+ chunk_size=50,
1298
+ )
1299
+
1265
1300
  def add_image(self, **params):
1266
1301
  post_data = {
1267
1302
  "title": "Test image",
@@ -1311,13 +1346,15 @@ class TestIssue613(WagtailTestUtils, TestCase):
1311
1346
  return image
1312
1347
 
1313
1348
  def test_issue_613_on_add(self):
1314
- # Reset the search index
1315
- self.search_backend.reset_index()
1316
- self.search_backend.add_type(Image)
1349
+ # Note to future developer troubleshooting this test...
1350
+ # This test previously started by calling self.search_backend.reset_index(), but that was evidently redundant because
1351
+ # this was broken on Elasticsearch prior to the fix in
1352
+ # https://github.com/wagtail/wagtailsearch/commit/53a98169bccc3cef5b234944037f2b3f78efafd4 .
1353
+ # If this turns out to be necessary after all, you might want to compare how wagtail.tests.test_page_search.PageSearchTests does it.
1317
1354
 
1318
1355
  # Add an image with some tags
1319
1356
  image = self.add_image(tags="hello")
1320
- self.search_backend.refresh_index()
1357
+ self.search_backend.refresh_indexes()
1321
1358
 
1322
1359
  # Search for it by tag
1323
1360
  results = self.search_backend.search("hello", Image)
@@ -1327,13 +1364,15 @@ class TestIssue613(WagtailTestUtils, TestCase):
1327
1364
  self.assertEqual(results[0].id, image.id)
1328
1365
 
1329
1366
  def test_issue_613_on_edit(self):
1330
- # Reset the search index
1331
- self.search_backend.reset_index()
1332
- self.search_backend.add_type(Image)
1367
+ # Note to future developer troubleshooting this test...
1368
+ # This test previously started by calling self.search_backend.reset_index(), but that was evidently redundant because
1369
+ # this was broken on Elasticsearch prior to the fix in
1370
+ # https://github.com/wagtail/wagtailsearch/commit/53a98169bccc3cef5b234944037f2b3f78efafd4 .
1371
+ # If this turns out to be necessary after all, you might want to compare how wagtail.tests.test_page_search.PageSearchTests does it.
1333
1372
 
1334
1373
  # Add an image with some tags
1335
1374
  image = self.edit_image(tags="hello")
1336
- self.search_backend.refresh_index()
1375
+ self.search_backend.refresh_indexes()
1337
1376
 
1338
1377
  # Search for it by tag
1339
1378
  results = self.search_backend.search("hello", Image)
@@ -10,6 +10,13 @@ from django.views.generic.base import View
10
10
  from wagtail.admin.auth import PermissionPolicyChecker
11
11
  from wagtail.admin.modal_workflow import render_modal_workflow
12
12
  from wagtail.admin.models import popular_tags_for_model
13
+ from wagtail.admin.ui.tables import (
14
+ BaseColumn,
15
+ Column,
16
+ DateColumn,
17
+ TitleColumn,
18
+ UsageCountColumn,
19
+ )
13
20
  from wagtail.admin.views.generic.chooser import (
14
21
  BaseChooseView,
15
22
  ChooseResultsViewMixin,
@@ -27,6 +34,7 @@ from wagtail.images.formats import get_image_format
27
34
  from wagtail.images.forms import ImageInsertionForm, get_image_form
28
35
  from wagtail.images.permissions import permission_policy
29
36
  from wagtail.images.utils import find_image_duplicates
37
+ from wagtail.models import ReferenceIndex
30
38
 
31
39
  permission_checker = PermissionPolicyChecker(permission_policy)
32
40
 
@@ -83,7 +91,8 @@ class BaseImageChooseView(BaseChooseView):
83
91
  return getattr(settings, "WAGTAILIMAGES_CHOOSER_PAGE_SIZE", 20)
84
92
 
85
93
  def get_object_list(self):
86
- return (
94
+ # Get images (filtered by user permission)
95
+ images = (
87
96
  permission_policy.instances_user_has_any_permission_for(
88
97
  self.request.user, ["choose"]
89
98
  )
@@ -91,6 +100,13 @@ class BaseImageChooseView(BaseChooseView):
91
100
  .prefetch_renditions("max-165x165")
92
101
  )
93
102
 
103
+ # Annotate with usage count from the ReferenceIndex
104
+ images = images.annotate(
105
+ usage_count=ReferenceIndex.usage_count_subquery(self.model)
106
+ )
107
+
108
+ return images
109
+
94
110
  def filter_object_list(self, objects):
95
111
  tag_name = self.request.GET.get("tag")
96
112
  if tag_name:
@@ -130,8 +146,53 @@ class BaseImageChooseView(BaseChooseView):
130
146
  )
131
147
 
132
148
  context["collections"] = self.collections
149
+ context["layout"] = self.layout
133
150
  return context
134
151
 
152
+ @cached_property
153
+ def layout(self):
154
+ return self.request.GET.get("layout", "grid")
155
+
156
+ @cached_property
157
+ def columns(self):
158
+ if self.layout == "grid":
159
+ return []
160
+ else:
161
+ columns = [
162
+ ImagePreviewColumn(
163
+ "preview",
164
+ label=_("Preview"),
165
+ accessor="image",
166
+ classname="image-preview",
167
+ ),
168
+ TitleColumnWithFilename(
169
+ "title",
170
+ label=_("Title"),
171
+ get_url=lambda obj: obj.chosen_url,
172
+ width="35%",
173
+ classname="title-with-filename",
174
+ ),
175
+ Column("collection", label=_("Collection"), accessor="collection.name"),
176
+ DateColumn(
177
+ "created_at",
178
+ label=_("Created"),
179
+ ),
180
+ UsageCountColumn(
181
+ "usage_count",
182
+ label=_("Usage"),
183
+ width="16%",
184
+ ),
185
+ ]
186
+ return columns
187
+
188
+
189
+ class ImagePreviewColumn(BaseColumn):
190
+ cell_template_name = "wagtailimages/chooser/image_preview_column_cell.html"
191
+
192
+
193
+ class TitleColumnWithFilename(TitleColumn):
194
+ cell_template_name = "wagtailimages/chooser/title_column_cell.html"
195
+
135
196
 
136
197
  class ImageChooseViewMixin(ChooseViewMixin):
137
198
  def get_context_data(self, **kwargs):
@@ -312,7 +373,10 @@ class ImageChooserViewSet(ChooserViewSet):
312
373
  select_format_view_class = ImageSelectFormatView
313
374
  permission_policy = permission_policy
314
375
  register_widget = False
315
- preserve_url_parameters = ChooserViewSet.preserve_url_parameters + ["select_format"]
376
+ preserve_url_parameters = ChooserViewSet.preserve_url_parameters + [
377
+ "select_format",
378
+ "layout",
379
+ ]
316
380
 
317
381
  icon = "image"
318
382
  choose_one_text = _("Choose an image")