wagtail 6.0.1__py3-none-any.whl → 6.1rc1__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 (512) hide show
  1. wagtail/__init__.py +1 -1
  2. wagtail/admin/checks.py +51 -0
  3. wagtail/admin/compare.py +1 -1
  4. wagtail/admin/filters.py +70 -1
  5. wagtail/admin/forms/account.py +1 -1
  6. wagtail/admin/forms/collections.py +15 -0
  7. wagtail/admin/forms/pages.py +49 -0
  8. wagtail/admin/locale/ca/LC_MESSAGES/django.mo +0 -0
  9. wagtail/admin/locale/ca/LC_MESSAGES/django.po +122 -0
  10. wagtail/admin/locale/de/LC_MESSAGES/django.mo +0 -0
  11. wagtail/admin/locale/de/LC_MESSAGES/django.po +5 -5
  12. wagtail/admin/locale/en/LC_MESSAGES/django.po +474 -385
  13. wagtail/admin/locale/en/LC_MESSAGES/djangojs.po +3 -3
  14. wagtail/admin/locale/es/LC_MESSAGES/django.mo +0 -0
  15. wagtail/admin/locale/es/LC_MESSAGES/django.po +6 -6
  16. wagtail/admin/locale/fr/LC_MESSAGES/django.mo +0 -0
  17. wagtail/admin/locale/fr/LC_MESSAGES/django.po +70 -3
  18. wagtail/admin/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  19. wagtail/admin/locale/he_IL/LC_MESSAGES/django.po +2 -6
  20. wagtail/admin/locale/he_IL/LC_MESSAGES/djangojs.mo +0 -0
  21. wagtail/admin/locale/he_IL/LC_MESSAGES/djangojs.po +2 -2
  22. wagtail/admin/locale/hr_HR/LC_MESSAGES/django.mo +0 -0
  23. wagtail/admin/locale/hr_HR/LC_MESSAGES/django.po +4 -0
  24. wagtail/admin/locale/hu/LC_MESSAGES/django.mo +0 -0
  25. wagtail/admin/locale/hu/LC_MESSAGES/django.po +142 -2
  26. wagtail/admin/locale/it/LC_MESSAGES/django.mo +0 -0
  27. wagtail/admin/locale/it/LC_MESSAGES/django.po +80 -8
  28. wagtail/admin/locale/it/LC_MESSAGES/djangojs.mo +0 -0
  29. wagtail/admin/locale/it/LC_MESSAGES/djangojs.po +14 -2
  30. wagtail/admin/locale/lv/LC_MESSAGES/django.mo +0 -0
  31. wagtail/admin/locale/lv/LC_MESSAGES/django.po +154 -1
  32. wagtail/admin/locale/pt_PT/LC_MESSAGES/django.mo +0 -0
  33. wagtail/admin/locale/pt_PT/LC_MESSAGES/django.po +73 -2
  34. wagtail/admin/locale/ro/LC_MESSAGES/django.mo +0 -0
  35. wagtail/admin/locale/ro/LC_MESSAGES/django.po +3 -3
  36. wagtail/admin/locale/sl/LC_MESSAGES/django.mo +0 -0
  37. wagtail/admin/locale/sl/LC_MESSAGES/django.po +145 -2
  38. wagtail/admin/locale/sv/LC_MESSAGES/django.mo +0 -0
  39. wagtail/admin/locale/sv/LC_MESSAGES/django.po +77 -3
  40. wagtail/admin/locale/zh_Hant/LC_MESSAGES/django.mo +0 -0
  41. wagtail/admin/locale/zh_Hant/LC_MESSAGES/django.po +17 -1
  42. wagtail/admin/panels/comment_panel.py +1 -1
  43. wagtail/admin/panels/field_panel.py +1 -1
  44. wagtail/admin/rich_text/converters/editor_html.py +3 -1
  45. wagtail/admin/rich_text/editors/draftail/__init__.py +28 -2
  46. wagtail/admin/static/wagtailadmin/css/core.css +1 -1
  47. wagtail/admin/static/wagtailadmin/css/panels/draftail.css +1 -1
  48. wagtail/admin/static/wagtailadmin/images/favicon.ico +0 -0
  49. wagtail/admin/static/wagtailadmin/js/bulk-actions.js +1 -1
  50. wagtail/admin/static/wagtailadmin/js/chooser-modal.js +1 -1
  51. wagtail/admin/static/wagtailadmin/js/chooser-widget-telepath.js +1 -1
  52. wagtail/admin/static/wagtailadmin/js/chooser-widget.js +1 -1
  53. wagtail/admin/static/wagtailadmin/js/comments.js +1 -1
  54. wagtail/admin/static/wagtailadmin/js/core.js +1 -1
  55. wagtail/admin/static/wagtailadmin/js/core.js.LICENSE.txt +1 -1
  56. wagtail/admin/static/wagtailadmin/js/date-time-chooser.js +1 -1
  57. wagtail/admin/static/wagtailadmin/js/draftail.js +1 -1
  58. wagtail/admin/static/wagtailadmin/js/expanding-formset.js +1 -1
  59. wagtail/admin/static/wagtailadmin/js/filtered-select.js +1 -1
  60. wagtail/admin/static/wagtailadmin/js/modal-workflow.js +1 -1
  61. wagtail/admin/static/wagtailadmin/js/page-chooser-modal.js +1 -1
  62. wagtail/admin/static/wagtailadmin/js/page-chooser-telepath.js +1 -1
  63. wagtail/admin/static/wagtailadmin/js/page-chooser.js +1 -1
  64. wagtail/admin/static/wagtailadmin/js/preview-panel.js +1 -1
  65. wagtail/admin/static/wagtailadmin/js/privacy-switch.js +1 -1
  66. wagtail/admin/static/wagtailadmin/js/sidebar.js +1 -1
  67. wagtail/admin/static/wagtailadmin/js/task-chooser-modal.js +1 -1
  68. wagtail/admin/static/wagtailadmin/js/task-chooser.js +1 -1
  69. wagtail/admin/static/wagtailadmin/js/telepath/blocks.js +1 -1
  70. wagtail/admin/static/wagtailadmin/js/telepath/telepath.js +1 -1
  71. wagtail/admin/static/wagtailadmin/js/telepath/widgets.js +1 -1
  72. wagtail/admin/static/wagtailadmin/js/userbar.js +1 -1
  73. wagtail/admin/static/wagtailadmin/js/vendor.js +1 -1
  74. wagtail/admin/static/wagtailadmin/js/vendor.js.LICENSE.txt +4 -4
  75. wagtail/admin/static/wagtailadmin/js/wagtailadmin.js +1 -1
  76. wagtail/admin/static/wagtailadmin/js/workflow-action.js +1 -1
  77. wagtail/admin/staticfiles.py +1 -0
  78. wagtail/admin/templates/wagtailadmin/admin_base.html +1 -0
  79. wagtail/admin/templates/wagtailadmin/base.html +1 -0
  80. wagtail/admin/templates/wagtailadmin/collection_privacy/set_privacy.html +3 -1
  81. wagtail/admin/templates/wagtailadmin/collections/edit.html +0 -1
  82. wagtail/admin/templates/wagtailadmin/collections/index_results.html +10 -0
  83. wagtail/admin/templates/wagtailadmin/generic/base.html +1 -9
  84. wagtail/admin/templates/wagtailadmin/generic/form.html +4 -2
  85. wagtail/admin/templates/wagtailadmin/generic/history/action_cell.html +27 -0
  86. wagtail/admin/templates/wagtailadmin/generic/index_results.html +8 -0
  87. wagtail/admin/templates/wagtailadmin/home/workflow_objects_to_moderate.html +3 -4
  88. wagtail/admin/templates/wagtailadmin/icons/keyboard.svg +1 -0
  89. wagtail/admin/templates/wagtailadmin/page_privacy/set_privacy.html +3 -1
  90. wagtail/admin/templates/wagtailadmin/pages/_editor_js.html +0 -15
  91. wagtail/admin/templates/wagtailadmin/pages/action_menu/save_draft.html +3 -1
  92. wagtail/admin/templates/wagtailadmin/pages/choose_parent.html +17 -0
  93. wagtail/admin/templates/wagtailadmin/pages/explorable_index.html +8 -0
  94. wagtail/admin/templates/wagtailadmin/pages/history.html +1 -61
  95. wagtail/admin/templates/wagtailadmin/pages/index.html +1 -5
  96. wagtail/admin/templates/wagtailadmin/pages/listing/_locked_indicator.html +2 -2
  97. wagtail/admin/templates/wagtailadmin/pages/listing/_page_title_column_header.html +25 -27
  98. wagtail/admin/templates/wagtailadmin/pages/page_listing_header.html +2 -1
  99. wagtail/admin/templates/wagtailadmin/panels/multi_field_panel_child.html +1 -1
  100. wagtail/admin/templates/wagtailadmin/panels/publishing/schedule_publishing_panel.html +3 -1
  101. wagtail/admin/templates/wagtailadmin/panels/tabbed_interface.html +1 -1
  102. wagtail/admin/templates/wagtailadmin/shared/active_filters.html +2 -1
  103. wagtail/admin/templates/wagtailadmin/shared/breadcrumbs.html +8 -0
  104. wagtail/admin/templates/wagtailadmin/shared/forms/single_checkbox.html +1 -1
  105. wagtail/admin/templates/wagtailadmin/shared/headers/page_edit_header.html +1 -1
  106. wagtail/admin/templates/wagtailadmin/shared/headers/slim_header.html +21 -9
  107. wagtail/admin/templates/wagtailadmin/shared/human_readable_date.html +1 -1
  108. wagtail/admin/templates/wagtailadmin/shared/keyboard_shortcuts_dialog.html +29 -0
  109. wagtail/admin/templates/wagtailadmin/shared/side_panel_toggle.html +2 -1
  110. wagtail/admin/templates/wagtailadmin/skeleton.html +2 -1
  111. wagtail/admin/templates/wagtailadmin/tables/related_objects_cell.html +9 -0
  112. wagtail/admin/templates/wagtailadmin/tables/title_cell.html +9 -7
  113. wagtail/admin/templates/wagtailadmin/widgets/draftail_rich_text_area.html +1 -1
  114. wagtail/admin/templates/wagtailadmin/workflows/create.html +6 -23
  115. wagtail/admin/templates/wagtailadmin/workflows/create_task.html +6 -15
  116. wagtail/admin/templates/wagtailadmin/workflows/edit.html +6 -23
  117. wagtail/admin/templates/wagtailadmin/workflows/edit_task.html +6 -13
  118. wagtail/admin/templates/wagtailadmin/workflows/includes/task_usage_cell.html +4 -4
  119. wagtail/admin/templates/wagtailadmin/workflows/includes/workflow_tasks_cell.html +18 -0
  120. wagtail/admin/templates/wagtailadmin/workflows/includes/workflow_title_cell.html +7 -0
  121. wagtail/admin/templates/wagtailadmin/workflows/includes/workflow_used_by_cell.html +25 -0
  122. wagtail/admin/templates/wagtailadmin/workflows/index.html +0 -99
  123. wagtail/admin/templates/wagtailadmin/workflows/index_results.html +10 -0
  124. wagtail/admin/templates/wagtailadmin/workflows/task_index.html +0 -30
  125. wagtail/admin/templates/wagtailadmin/workflows/task_index_results.html +10 -0
  126. wagtail/admin/templates/wagtailadmin/workflows/usage.html +1 -1
  127. wagtail/admin/templatetags/wagtailadmin_tags.py +116 -39
  128. wagtail/admin/tests/api/test_pages.py +26 -10
  129. wagtail/admin/tests/pages/test_create_page.py +10 -4
  130. wagtail/admin/tests/pages/test_custom_listing.py +37 -0
  131. wagtail/admin/tests/pages/test_edit_page.py +6 -6
  132. wagtail/admin/tests/pages/test_explorer_view.py +19 -18
  133. wagtail/admin/tests/pages/test_move_page.py +1 -1
  134. wagtail/admin/tests/pages/test_page_usage.py +50 -2
  135. wagtail/admin/tests/pages/test_parent_page_chooser_view.py +119 -0
  136. wagtail/admin/tests/pages/test_preview.py +18 -4
  137. wagtail/admin/tests/test_account_management.py +20 -1
  138. wagtail/admin/tests/test_audit_log.py +172 -5
  139. wagtail/admin/tests/test_checks.py +92 -0
  140. wagtail/admin/tests/test_collections_views.py +19 -5
  141. wagtail/admin/tests/test_compare.py +6 -6
  142. wagtail/admin/tests/test_dashboard.py +404 -0
  143. wagtail/admin/tests/test_dbwhitelister.py +4 -5
  144. wagtail/admin/tests/test_edit_handlers.py +2 -2
  145. wagtail/admin/tests/test_keyboard_shortcuts.py +84 -0
  146. wagtail/admin/tests/test_page_chooser.py +31 -18
  147. wagtail/admin/tests/test_privacy.py +36 -2
  148. wagtail/admin/tests/test_rich_text.py +168 -23
  149. wagtail/admin/tests/test_templatetags.py +411 -43
  150. wagtail/admin/tests/test_views.py +4 -2
  151. wagtail/admin/tests/test_workflows.py +531 -9
  152. wagtail/admin/tests/tests.py +3 -1
  153. wagtail/admin/tests/ui/test_tables.py +48 -1
  154. wagtail/admin/tests/viewsets/test_model_viewset.py +130 -23
  155. wagtail/admin/ui/side_panels.py +3 -1
  156. wagtail/admin/ui/tables/__init__.py +13 -1
  157. wagtail/admin/ui/tables/pages.py +17 -6
  158. wagtail/admin/urls/__init__.py +8 -3
  159. wagtail/admin/urls/pages.py +5 -0
  160. wagtail/admin/urls/workflows.py +10 -0
  161. wagtail/admin/views/chooser.py +20 -24
  162. wagtail/admin/views/collections.py +17 -1
  163. wagtail/admin/views/generic/base.py +34 -4
  164. wagtail/admin/views/generic/history.py +220 -51
  165. wagtail/admin/views/generic/mixins.py +7 -4
  166. wagtail/admin/views/generic/models.py +54 -47
  167. wagtail/admin/views/generic/multiple_upload.py +17 -8
  168. wagtail/admin/views/generic/usage.py +17 -11
  169. wagtail/admin/views/home.py +15 -12
  170. wagtail/admin/views/mixins.py +30 -0
  171. wagtail/admin/views/pages/choose_parent.py +73 -0
  172. wagtail/admin/views/pages/history.py +54 -66
  173. wagtail/admin/views/pages/listing.py +187 -106
  174. wagtail/admin/views/pages/usage.py +6 -1
  175. wagtail/admin/views/pages/utils.py +70 -1
  176. wagtail/admin/views/workflows.py +150 -21
  177. wagtail/admin/viewsets/model.py +2 -2
  178. wagtail/admin/viewsets/pages.py +77 -0
  179. wagtail/admin/wagtail_hooks.py +40 -2
  180. wagtail/admin/widgets/button.py +10 -10
  181. wagtail/api/v2/filters.py +1 -1
  182. wagtail/api/v2/tests/test_pages.py +1 -1
  183. wagtail/blocks/base.py +18 -9
  184. wagtail/blocks/field_block.py +9 -7
  185. wagtail/blocks/list_block.py +16 -6
  186. wagtail/blocks/static_block.py +3 -0
  187. wagtail/blocks/stream_block.py +58 -23
  188. wagtail/blocks/struct_block.py +15 -9
  189. wagtail/contrib/forms/locale/en/LC_MESSAGES/django.po +39 -47
  190. wagtail/contrib/forms/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  191. wagtail/contrib/forms/locale/he_IL/LC_MESSAGES/django.po +2 -2
  192. wagtail/contrib/forms/models.py +5 -5
  193. wagtail/contrib/forms/templates/wagtailforms/list_submissions.html +44 -33
  194. wagtail/contrib/forms/templates/wagtailforms/submissions_index.html +2 -63
  195. wagtail/contrib/forms/tests/test_models.py +26 -0
  196. wagtail/contrib/forms/urls.py +6 -0
  197. wagtail/contrib/forms/views.py +52 -49
  198. wagtail/contrib/redirects/locale/ca/LC_MESSAGES/django.mo +0 -0
  199. wagtail/contrib/redirects/locale/ca/LC_MESSAGES/django.po +3 -3
  200. wagtail/contrib/redirects/locale/en/LC_MESSAGES/django.po +34 -42
  201. wagtail/contrib/redirects/locale/fr/LC_MESSAGES/django.po +2 -2
  202. wagtail/contrib/redirects/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  203. wagtail/contrib/redirects/locale/he_IL/LC_MESSAGES/django.po +2 -2
  204. wagtail/contrib/redirects/signal_handlers.py +1 -1
  205. wagtail/contrib/redirects/templates/wagtailredirects/index.html +1 -36
  206. wagtail/contrib/redirects/templates/wagtailredirects/index_results.html +18 -0
  207. wagtail/contrib/redirects/templates/wagtailredirects/redirect_target_cell.html +8 -0
  208. wagtail/contrib/redirects/tests/test_import_command.py +1 -1
  209. wagtail/contrib/redirects/tests/test_redirects.py +79 -8
  210. wagtail/contrib/redirects/urls.py +2 -1
  211. wagtail/contrib/redirects/views.py +85 -55
  212. wagtail/contrib/search_promotions/admin_urls.py +2 -1
  213. wagtail/contrib/search_promotions/locale/en/LC_MESSAGES/django.po +41 -64
  214. wagtail/contrib/search_promotions/locale/fr/LC_MESSAGES/django.po +2 -2
  215. wagtail/contrib/search_promotions/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  216. wagtail/contrib/search_promotions/locale/he_IL/LC_MESSAGES/django.po +2 -2
  217. wagtail/contrib/search_promotions/locale/hr_HR/LC_MESSAGES/django.mo +0 -0
  218. wagtail/contrib/search_promotions/locale/hr_HR/LC_MESSAGES/django.po +41 -2
  219. wagtail/contrib/search_promotions/locale/it/LC_MESSAGES/django.mo +0 -0
  220. wagtail/contrib/search_promotions/locale/it/LC_MESSAGES/django.po +9 -3
  221. wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/index.html +1 -16
  222. wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/index_results.html +11 -0
  223. wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/list.html +0 -51
  224. wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/results.html +3 -16
  225. wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/search_promotion_column.html +15 -0
  226. wagtail/contrib/search_promotions/tests.py +122 -9
  227. wagtail/contrib/search_promotions/views.py +66 -65
  228. wagtail/contrib/settings/locale/en/LC_MESSAGES/django.po +3 -3
  229. wagtail/contrib/settings/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  230. wagtail/contrib/settings/locale/he_IL/LC_MESSAGES/django.po +2 -2
  231. wagtail/contrib/settings/locale/tr/LC_MESSAGES/django.mo +0 -0
  232. wagtail/contrib/settings/locale/tr/LC_MESSAGES/django.po +6 -2
  233. wagtail/contrib/settings/registry.py +10 -5
  234. wagtail/contrib/settings/tests/generic/test_admin.py +9 -0
  235. wagtail/contrib/settings/tests/site_specific/test_admin.py +10 -1
  236. wagtail/contrib/settings/tests/site_specific/test_model.py +3 -3
  237. wagtail/contrib/settings/tests/site_specific/test_templates.py +1 -1
  238. wagtail/contrib/settings/views.py +3 -1
  239. wagtail/contrib/simple_translation/locale/en/LC_MESSAGES/django.po +1 -1
  240. wagtail/contrib/simple_translation/tests/test_wagtail_hooks.py +2 -2
  241. wagtail/contrib/styleguide/locale/en/LC_MESSAGES/django.po +7 -7
  242. wagtail/contrib/styleguide/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  243. wagtail/contrib/styleguide/locale/he_IL/LC_MESSAGES/django.po +2 -2
  244. wagtail/contrib/table_block/blocks.py +2 -2
  245. wagtail/contrib/table_block/locale/ca/LC_MESSAGES/django.mo +0 -0
  246. wagtail/contrib/table_block/locale/ca/LC_MESSAGES/django.po +27 -2
  247. wagtail/contrib/table_block/locale/en/LC_MESSAGES/django.po +1 -1
  248. wagtail/contrib/table_block/locale/hu/LC_MESSAGES/django.mo +0 -0
  249. wagtail/contrib/table_block/locale/hu/LC_MESSAGES/django.po +27 -2
  250. wagtail/contrib/table_block/locale/it/LC_MESSAGES/django.mo +0 -0
  251. wagtail/contrib/table_block/locale/it/LC_MESSAGES/django.po +27 -2
  252. wagtail/contrib/table_block/static/table_block/js/table.js +1 -1
  253. wagtail/contrib/table_block/tests.py +6 -0
  254. wagtail/contrib/typed_table_block/locale/ca/LC_MESSAGES/django.mo +0 -0
  255. wagtail/contrib/typed_table_block/locale/ca/LC_MESSAGES/django.po +12 -2
  256. wagtail/contrib/typed_table_block/locale/en/LC_MESSAGES/django.po +1 -1
  257. wagtail/contrib/typed_table_block/locale/hu/LC_MESSAGES/django.mo +0 -0
  258. wagtail/contrib/typed_table_block/locale/hu/LC_MESSAGES/django.po +12 -2
  259. wagtail/contrib/typed_table_block/locale/it/LC_MESSAGES/django.mo +0 -0
  260. wagtail/contrib/typed_table_block/locale/it/LC_MESSAGES/django.po +12 -2
  261. wagtail/contrib/typed_table_block/static/typed_table_block/js/typed_table_block.js +1 -1
  262. wagtail/coreutils.py +3 -2
  263. wagtail/documents/admin_urls.py +2 -2
  264. wagtail/documents/locale/en/LC_MESSAGES/django.po +22 -22
  265. wagtail/documents/locale/fr/LC_MESSAGES/django.po +2 -2
  266. wagtail/documents/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  267. wagtail/documents/locale/he_IL/LC_MESSAGES/django.po +2 -2
  268. wagtail/documents/locale/hr_HR/LC_MESSAGES/django.mo +0 -0
  269. wagtail/documents/locale/hr_HR/LC_MESSAGES/django.po +19 -2
  270. wagtail/documents/locale/hu/LC_MESSAGES/django.mo +0 -0
  271. wagtail/documents/locale/hu/LC_MESSAGES/django.po +16 -2
  272. wagtail/documents/locale/it/LC_MESSAGES/django.mo +0 -0
  273. wagtail/documents/locale/it/LC_MESSAGES/django.po +19 -2
  274. wagtail/documents/migrations/0013_delete_uploadeddocument.py +16 -0
  275. wagtail/documents/models.py +1 -20
  276. wagtail/documents/rich_text/__init__.py +11 -7
  277. wagtail/documents/static/wagtaildocs/js/document-chooser-modal.js +1 -1
  278. wagtail/documents/static/wagtaildocs/js/document-chooser-telepath.js +1 -1
  279. wagtail/documents/static/wagtaildocs/js/document-chooser.js +1 -1
  280. wagtail/documents/templates/wagtaildocs/documents/index.html +0 -16
  281. wagtail/documents/tests/test_admin_views.py +155 -23
  282. wagtail/documents/tests/test_collection_privacy.py +55 -1
  283. wagtail/documents/tests/test_rich_text.py +14 -0
  284. wagtail/documents/views/documents.py +25 -22
  285. wagtail/documents/views/multiple.py +6 -7
  286. wagtail/documents/views/serve.py +16 -1
  287. wagtail/documents/wagtail_hooks.py +20 -15
  288. wagtail/embeds/blocks.py +5 -0
  289. wagtail/embeds/locale/en/LC_MESSAGES/django.po +2 -2
  290. wagtail/embeds/locale/fr/LC_MESSAGES/django.po +2 -2
  291. wagtail/embeds/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  292. wagtail/embeds/locale/he_IL/LC_MESSAGES/django.po +2 -2
  293. wagtail/embeds/rich_text/__init__.py +1 -1
  294. wagtail/embeds/tests/test_rich_text.py +14 -0
  295. wagtail/embeds/wagtail_hooks.py +4 -14
  296. wagtail/fields.py +3 -48
  297. wagtail/images/admin_urls.py +2 -2
  298. wagtail/images/check_files/wagtail.jpg +0 -0
  299. wagtail/images/check_files/wagtail.png +0 -0
  300. wagtail/images/fields.py +2 -0
  301. wagtail/images/image_operations.py +1 -1
  302. wagtail/images/locale/ca/LC_MESSAGES/django.mo +0 -0
  303. wagtail/images/locale/ca/LC_MESSAGES/django.po +12 -0
  304. wagtail/images/locale/en/LC_MESSAGES/django.po +33 -45
  305. wagtail/images/locale/fr/LC_MESSAGES/django.po +2 -2
  306. wagtail/images/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  307. wagtail/images/locale/he_IL/LC_MESSAGES/django.po +2 -2
  308. wagtail/images/locale/hu/LC_MESSAGES/django.mo +0 -0
  309. wagtail/images/locale/hu/LC_MESSAGES/django.po +28 -2
  310. wagtail/images/locale/it/LC_MESSAGES/django.mo +0 -0
  311. wagtail/images/locale/it/LC_MESSAGES/django.po +14 -2
  312. wagtail/images/locale/pt_PT/LC_MESSAGES/django.mo +0 -0
  313. wagtail/images/locale/pt_PT/LC_MESSAGES/django.po +4 -0
  314. wagtail/images/migrations/0026_delete_uploadedimage.py +16 -0
  315. wagtail/images/models.py +49 -43
  316. wagtail/images/rich_text/__init__.py +18 -8
  317. wagtail/images/static/wagtailimages/js/image-chooser-modal.js +1 -1
  318. wagtail/images/static/wagtailimages/js/image-chooser-telepath.js +1 -1
  319. wagtail/images/static/wagtailimages/js/image-chooser.js +1 -1
  320. wagtail/images/templates/wagtailimages/images/image_listing_header.html +6 -0
  321. wagtail/images/templates/wagtailimages/images/index.html +11 -51
  322. wagtail/images/tests/test_admin_views.py +119 -62
  323. wagtail/images/tests/test_image_operations.py +10 -0
  324. wagtail/images/tests/test_models.py +35 -33
  325. wagtail/images/tests/test_rich_text.py +14 -0
  326. wagtail/images/tests/utils.py +1 -1
  327. wagtail/images/views/images.py +35 -64
  328. wagtail/images/views/multiple.py +6 -7
  329. wagtail/images/wagtail_hooks.py +4 -14
  330. wagtail/locale/en/LC_MESSAGES/django.po +150 -136
  331. wagtail/locale/es/LC_MESSAGES/django.mo +0 -0
  332. wagtail/locale/es/LC_MESSAGES/django.po +3 -2
  333. wagtail/locale/fr/LC_MESSAGES/django.po +2 -2
  334. wagtail/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  335. wagtail/locale/he_IL/LC_MESSAGES/django.po +2 -2
  336. wagtail/locale/it/LC_MESSAGES/django.mo +0 -0
  337. wagtail/locale/it/LC_MESSAGES/django.po +5 -5
  338. wagtail/locale/sl/LC_MESSAGES/django.mo +0 -0
  339. wagtail/locale/sl/LC_MESSAGES/django.po +27 -2
  340. wagtail/locales/locale/ar/LC_MESSAGES/django.po +1 -1
  341. wagtail/locales/locale/be/LC_MESSAGES/django.po +1 -1
  342. wagtail/locales/locale/bg/LC_MESSAGES/django.po +1 -1
  343. wagtail/locales/locale/ca/LC_MESSAGES/django.po +1 -1
  344. wagtail/locales/locale/cs/LC_MESSAGES/django.po +1 -1
  345. wagtail/locales/locale/cy/LC_MESSAGES/django.po +1 -1
  346. wagtail/locales/locale/da/LC_MESSAGES/django.po +1 -1
  347. wagtail/locales/locale/de/LC_MESSAGES/django.po +1 -1
  348. wagtail/locales/locale/el/LC_MESSAGES/django.po +1 -1
  349. wagtail/locales/locale/en/LC_MESSAGES/django.po +1 -1
  350. wagtail/locales/locale/es/LC_MESSAGES/django.po +1 -1
  351. wagtail/locales/locale/et/LC_MESSAGES/django.po +2 -2
  352. wagtail/locales/locale/fa/LC_MESSAGES/django.po +1 -1
  353. wagtail/locales/locale/fi/LC_MESSAGES/django.po +1 -1
  354. wagtail/locales/locale/fr/LC_MESSAGES/django.po +1 -1
  355. wagtail/locales/locale/gl/LC_MESSAGES/django.po +1 -1
  356. wagtail/locales/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  357. wagtail/locales/locale/he_IL/LC_MESSAGES/django.po +3 -3
  358. wagtail/locales/locale/hr_HR/LC_MESSAGES/django.po +1 -1
  359. wagtail/locales/locale/hu/LC_MESSAGES/django.po +1 -1
  360. wagtail/locales/locale/id_ID/LC_MESSAGES/django.po +1 -1
  361. wagtail/locales/locale/is_IS/LC_MESSAGES/django.po +1 -1
  362. wagtail/locales/locale/it/LC_MESSAGES/django.po +1 -1
  363. wagtail/locales/locale/ja/LC_MESSAGES/django.po +1 -1
  364. wagtail/locales/locale/ko/LC_MESSAGES/django.po +1 -1
  365. wagtail/locales/locale/lt/LC_MESSAGES/django.po +1 -1
  366. wagtail/locales/locale/lv/LC_MESSAGES/django.po +1 -1
  367. wagtail/locales/locale/mi/LC_MESSAGES/django.po +1 -1
  368. wagtail/locales/locale/mn/LC_MESSAGES/django.po +1 -1
  369. wagtail/locales/locale/my/LC_MESSAGES/django.po +1 -1
  370. wagtail/locales/locale/nb/LC_MESSAGES/django.po +1 -1
  371. wagtail/locales/locale/nl/LC_MESSAGES/django.po +1 -1
  372. wagtail/locales/locale/pl/LC_MESSAGES/django.po +1 -1
  373. wagtail/locales/locale/pt_BR/LC_MESSAGES/django.po +1 -1
  374. wagtail/locales/locale/pt_PT/LC_MESSAGES/django.po +1 -1
  375. wagtail/locales/locale/ro/LC_MESSAGES/django.po +1 -1
  376. wagtail/locales/locale/ru/LC_MESSAGES/django.po +1 -1
  377. wagtail/locales/locale/sk_SK/LC_MESSAGES/django.po +1 -1
  378. wagtail/locales/locale/sl/LC_MESSAGES/django.po +1 -1
  379. wagtail/locales/locale/sv/LC_MESSAGES/django.po +1 -1
  380. wagtail/locales/locale/tet/LC_MESSAGES/django.po +1 -1
  381. wagtail/locales/locale/th/LC_MESSAGES/django.po +1 -1
  382. wagtail/locales/locale/tr/LC_MESSAGES/django.po +1 -1
  383. wagtail/locales/locale/tr_TR/LC_MESSAGES/django.po +1 -1
  384. wagtail/locales/locale/uk/LC_MESSAGES/django.po +1 -1
  385. wagtail/locales/locale/vi/LC_MESSAGES/django.po +1 -1
  386. wagtail/locales/locale/zh/LC_MESSAGES/django.po +1 -1
  387. wagtail/locales/locale/zh_Hans/LC_MESSAGES/django.po +1 -1
  388. wagtail/locales/locale/zh_Hant/LC_MESSAGES/django.po +1 -1
  389. wagtail/locales/tests.py +18 -3
  390. wagtail/locales/views.py +0 -1
  391. wagtail/management/commands/rebuild_references_index.py +3 -1
  392. wagtail/migrations/0092_alter_collectionviewrestriction_password_and_more.py +33 -0
  393. wagtail/migrations/0093_uploadedfile.py +53 -0
  394. wagtail/models/__init__.py +147 -32
  395. wagtail/models/i18n.py +1 -1
  396. wagtail/models/{collections.py → media.py} +33 -2
  397. wagtail/models/reference_index.py +1 -1
  398. wagtail/models/view_restrictions.py +10 -3
  399. wagtail/project_template/project_name/settings/base.py +6 -0
  400. wagtail/project_template/requirements.txt +1 -1
  401. wagtail/rich_text/__init__.py +25 -8
  402. wagtail/rich_text/pages.py +19 -8
  403. wagtail/rich_text/rewriters.py +140 -68
  404. wagtail/search/backends/database/mysql/mysql.py +3 -3
  405. wagtail/search/backends/database/postgres/postgres.py +3 -3
  406. wagtail/search/backends/database/sqlite/sqlite.py +2 -2
  407. wagtail/search/backends/elasticsearch7.py +4 -0
  408. wagtail/search/locale/en/LC_MESSAGES/django.po +3 -3
  409. wagtail/search/tests/test_postgres_backend.py +50 -0
  410. wagtail/sites/locale/en/LC_MESSAGES/django.po +8 -8
  411. wagtail/sites/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  412. wagtail/sites/locale/he_IL/LC_MESSAGES/django.po +2 -2
  413. wagtail/sites/locale/ro/LC_MESSAGES/django.mo +0 -0
  414. wagtail/sites/locale/ro/LC_MESSAGES/django.po +3 -2
  415. wagtail/sites/tests.py +35 -9
  416. wagtail/sites/views.py +3 -1
  417. wagtail/snippets/locale/de/LC_MESSAGES/django.mo +0 -0
  418. wagtail/snippets/locale/de/LC_MESSAGES/django.po +7 -8
  419. wagtail/snippets/locale/en/LC_MESSAGES/django.po +16 -56
  420. wagtail/snippets/locale/fr/LC_MESSAGES/django.po +2 -2
  421. wagtail/snippets/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  422. wagtail/snippets/locale/he_IL/LC_MESSAGES/django.po +2 -2
  423. wagtail/snippets/locale/hr_HR/LC_MESSAGES/django.mo +0 -0
  424. wagtail/snippets/locale/hr_HR/LC_MESSAGES/django.po +6 -2
  425. wagtail/snippets/locale/lv/LC_MESSAGES/django.mo +0 -0
  426. wagtail/snippets/locale/lv/LC_MESSAGES/django.po +12 -0
  427. wagtail/snippets/locale/zh_Hant/LC_MESSAGES/django.mo +0 -0
  428. wagtail/snippets/locale/zh_Hant/LC_MESSAGES/django.po +4 -0
  429. wagtail/snippets/static/wagtailsnippets/js/snippet-chooser-telepath.js +1 -1
  430. wagtail/snippets/static/wagtailsnippets/js/snippet-chooser.js +1 -1
  431. wagtail/snippets/templates/wagtailsnippets/snippets/action_menu/publish.html +3 -1
  432. wagtail/snippets/templates/wagtailsnippets/snippets/action_menu/save.html +3 -1
  433. wagtail/snippets/templates/wagtailsnippets/snippets/create.html +2 -3
  434. wagtail/snippets/templates/wagtailsnippets/snippets/edit.html +2 -3
  435. wagtail/snippets/tests/test_preview.py +13 -2
  436. wagtail/snippets/tests/test_snippets.py +41 -16
  437. wagtail/snippets/tests/test_viewset.py +95 -18
  438. wagtail/snippets/tests/test_workflows.py +12 -0
  439. wagtail/snippets/views/snippets.py +1 -40
  440. wagtail/templatetags/wagtailcore_tags.py +1 -1
  441. wagtail/test/demosite/models.py +1 -1
  442. wagtail/test/middleware.py +14 -1
  443. wagtail/test/testapp/fixtures/test.json +20 -0
  444. wagtail/test/testapp/migrations/0001_squashed_0073_revisablechildmodel_secret_text.py +8 -8
  445. wagtail/test/testapp/migrations/0023_snippetchoosermodel_full_featured.py +1 -0
  446. wagtail/test/testapp/migrations/0034_custompermissionmodel.py +44 -0
  447. wagtail/test/testapp/migrations/0035_modelwithcustommanager.py +30 -0
  448. wagtail/test/testapp/migrations/0036_complexdefaultstreampage.py +28 -0
  449. wagtail/test/testapp/models.py +79 -2
  450. wagtail/test/testapp/templates/tests/custom_docs_password_required.html +10 -0
  451. wagtail/test/testapp/templates/tests/custom_page_password_required.html +10 -0
  452. wagtail/test/testapp/views.py +24 -2
  453. wagtail/test/testapp/wagtail_hooks.py +19 -0
  454. wagtail/test/utils/wagtail_tests.py +2 -2
  455. wagtail/tests/test_blocks.py +262 -1
  456. wagtail/tests/test_migrations.py +1 -1
  457. wagtail/tests/test_page_model.py +77 -0
  458. wagtail/tests/test_page_privacy.py +18 -1
  459. wagtail/tests/test_rich_text.py +95 -5
  460. wagtail/tests/test_streamfield.py +43 -0
  461. wagtail/tests/test_utils.py +8 -2
  462. wagtail/tests/test_views.py +52 -1
  463. wagtail/tests/test_whitelist.py +7 -7
  464. wagtail/users/forms.py +3 -1
  465. wagtail/users/locale/en/LC_MESSAGES/django.po +124 -96
  466. wagtail/users/locale/fr/LC_MESSAGES/django.po +2 -2
  467. wagtail/users/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  468. wagtail/users/locale/he_IL/LC_MESSAGES/django.po +2 -2
  469. wagtail/users/locale/hr_HR/LC_MESSAGES/django.mo +0 -0
  470. wagtail/users/locale/hr_HR/LC_MESSAGES/django.po +13 -2
  471. wagtail/users/migrations/0013_userprofile_density.py +23 -0
  472. wagtail/users/models.py +14 -3
  473. wagtail/users/templates/wagtailusers/groups/create.html +1 -7
  474. wagtail/users/templates/wagtailusers/groups/edit.html +1 -13
  475. wagtail/users/templates/wagtailusers/groups/includes/formatted_permissions.html +46 -2
  476. wagtail/users/templates/wagtailusers/groups/includes/group_form_js.html +0 -3
  477. wagtail/users/templates/wagtailusers/users/create.html +1 -14
  478. wagtail/users/templates/wagtailusers/users/edit.html +1 -14
  479. wagtail/users/templates/wagtailusers/users/index.html +2 -5
  480. wagtail/users/templates/wagtailusers/users/index_results.html +3 -13
  481. wagtail/users/templates/wagtailusers/users/user_cell.html +9 -0
  482. wagtail/users/templatetags/wagtailusers_tags.py +107 -20
  483. wagtail/users/tests/test_admin_views.py +669 -90
  484. wagtail/users/views/groups.py +58 -61
  485. wagtail/users/views/users.py +211 -92
  486. wagtail/users/wagtail_hooks.py +6 -38
  487. wagtail/users/widgets.py +3 -5
  488. wagtail/utils/text.py +1 -1
  489. wagtail/views.py +5 -9
  490. wagtail/whitelist.py +1 -1
  491. {wagtail-6.0.1.dist-info → wagtail-6.1rc1.dist-info}/METADATA +5 -6
  492. {wagtail-6.0.1.dist-info → wagtail-6.1rc1.dist-info}/RECORD +496 -477
  493. wagtail/admin/static/wagtailadmin/js/page-editor.js +0 -1
  494. wagtail/admin/static/wagtailadmin/js/vendor/mousetrap.min.js +0 -1
  495. wagtail/admin/static/wagtailadmin/js/vendor/urlify.js +0 -1
  496. wagtail/admin/static/wagtailadmin/js/vendor/xregexp.min.js +0 -1
  497. wagtail/admin/templates/wagtailadmin/collections/index.html +0 -34
  498. wagtail/admin/templates/wagtailadmin/pages/revisions/_actions.html +0 -22
  499. wagtail/admin/templates/wagtailadmin/shared/page_breadcrumbs.html +0 -55
  500. wagtail/admin/tests/pages/test_dashboard.py +0 -172
  501. wagtail/contrib/redirects/templates/wagtailredirects/results.html +0 -23
  502. wagtail/documents/templates/wagtaildocs/documents/list.html +0 -2
  503. wagtail/search/tests/test_postgres_stemming.py +0 -40
  504. wagtail/sites/templates/wagtailsites/create.html +0 -7
  505. wagtail/sites/templates/wagtailsites/edit.html +0 -7
  506. wagtail/snippets/templates/wagtailsnippets/snippets/revisions/_actions.html +0 -36
  507. wagtail/users/templates/wagtailusers/users/list.html +0 -62
  508. wagtail/users/urls/users.py +0 -12
  509. {wagtail-6.0.1.dist-info → wagtail-6.1rc1.dist-info}/LICENSE +0 -0
  510. {wagtail-6.0.1.dist-info → wagtail-6.1rc1.dist-info}/WHEEL +0 -0
  511. {wagtail-6.0.1.dist-info → wagtail-6.1rc1.dist-info}/entry_points.txt +0 -0
  512. {wagtail-6.0.1.dist-info → wagtail-6.1rc1.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,7 @@
1
1
  import re
2
2
  from functools import lru_cache
3
3
  from html import unescape
4
+ from typing import List
4
5
 
5
6
  from django.core.validators import MaxLengthValidator
6
7
  from django.db.models import Model
@@ -26,23 +27,23 @@ def get_rewriter():
26
27
  return MultiRuleRewriter(
27
28
  [
28
29
  LinkRewriter(
29
- {
30
- linktype: handler.expand_db_attributes
30
+ bulk_rules={
31
+ linktype: handler.expand_db_attributes_many
31
32
  for linktype, handler in link_rules.items()
32
33
  },
33
- {
34
+ reference_extractors={
34
35
  linktype: handler.extract_references
35
36
  for linktype, handler in link_rules.items()
36
37
  },
37
38
  ),
38
39
  EmbedRewriter(
39
- {
40
- embedtype: handler.expand_db_attributes
40
+ bulk_rules={
41
+ embedtype: handler.expand_db_attributes_many
41
42
  for embedtype, handler in embed_rules.items()
42
43
  },
43
- {
44
- linktype: handler.extract_references
45
- for linktype, handler in embed_rules.items()
44
+ reference_extractors={
45
+ embedtype: handler.extract_references
46
+ for embedtype, handler in embed_rules.items()
46
47
  },
47
48
  ),
48
49
  ]
@@ -130,6 +131,14 @@ class EntityHandler:
130
131
  model = cls.get_model()
131
132
  return model._default_manager.get(id=attrs["id"])
132
133
 
134
+ @classmethod
135
+ def get_many(cls, attrs_list: List[dict]) -> List[Model]:
136
+ model = cls.get_model()
137
+ instance_ids = [attrs.get("id") for attrs in attrs_list]
138
+ instances_by_id = model._default_manager.in_bulk(instance_ids)
139
+ instances_by_str_id = {str(k): v for k, v in instances_by_id.items()}
140
+ return [instances_by_str_id.get(str(id_)) for id_ in instance_ids]
141
+
133
142
  @staticmethod
134
143
  def expand_db_attributes(attrs: dict) -> str:
135
144
  """
@@ -138,6 +147,14 @@ class EntityHandler:
138
147
  """
139
148
  raise NotImplementedError
140
149
 
150
+ @classmethod
151
+ def expand_db_attributes_many(cls, attrs_list: List[dict]) -> List[str]:
152
+ """
153
+ Given a list of attribute dicts from a list of entity tags stored in
154
+ the database, return the real HTML representation of each one.
155
+ """
156
+ return list(map(cls.expand_db_attributes, attrs_list))
157
+
141
158
  @classmethod
142
159
  def extract_references(cls, attrs):
143
160
  """
@@ -1,3 +1,6 @@
1
+ from typing import List
2
+
3
+ from django.db.models import Model
1
4
  from django.utils.html import escape
2
5
 
3
6
  from wagtail.models import Page
@@ -12,16 +15,24 @@ class PageLinkHandler(LinkHandler):
12
15
  return Page
13
16
 
14
17
  @classmethod
15
- def get_instance(cls, attrs):
16
- return super().get_instance(attrs).specific
18
+ def get_many(cls, attrs_list: List[dict]) -> List[Model]:
19
+ # Override LinkHandler.get_many to reduce database queries through the
20
+ # use of PageQuerySet.specific() instead of QuerySet.in_bulk().
21
+ instance_ids = [attrs.get("id") for attrs in attrs_list]
22
+ qs = Page.objects.filter(id__in=instance_ids).defer_streamfields().specific()
23
+ pages_by_str_id = {str(page.id): page for page in qs}
24
+ return [pages_by_str_id.get(str(id_)) for id_ in instance_ids]
25
+
26
+ @classmethod
27
+ def expand_db_attributes(cls, attrs: dict) -> str:
28
+ return cls.expand_db_attributes_many([attrs])[0]
17
29
 
18
30
  @classmethod
19
- def expand_db_attributes(cls, attrs):
20
- try:
21
- page = cls.get_instance(attrs)
22
- return '<a href="%s">' % escape(page.localized.specific.url)
23
- except Page.DoesNotExist:
24
- return "<a>"
31
+ def expand_db_attributes_many(cls, attrs_list: List[dict]) -> List[str]:
32
+ return [
33
+ '<a href="%s">' % escape(page.localized.url) if page else "<a>"
34
+ for page in cls.get_many(attrs_list)
35
+ ]
25
36
 
26
37
  @classmethod
27
38
  def extract_references(self, attrs):
@@ -3,13 +3,16 @@ Utility classes for rewriting elements of HTML-like strings
3
3
  """
4
4
 
5
5
  import re
6
+ from collections import defaultdict
7
+ from itertools import chain
8
+ from typing import Callable, Tuple
6
9
 
7
10
  FIND_A_TAG = re.compile(r"<a(\b[^>]*)>")
8
11
  FIND_EMBED_TAG = re.compile(r"<embed(\b[^>]*)/>")
9
12
  FIND_ATTRS = re.compile(r'([\w-]+)\="([^"]*)"')
10
13
 
11
14
 
12
- def extract_attrs(attr_string):
15
+ def extract_attrs(attr_string: str) -> dict:
13
16
  """
14
17
  helper method to extract tag attributes, as a dict of un-escaped strings
15
18
  """
@@ -25,101 +28,170 @@ def extract_attrs(attr_string):
25
28
  return attributes
26
29
 
27
30
 
28
- class EmbedRewriter:
29
- """
30
- Rewrites <embed embedtype="foo" /> tags within rich text into the HTML fragment given by the
31
- embed rule for 'foo'. Each embed rule is a function that takes a dict of attributes and
32
- returns the HTML fragment.
33
- """
34
-
35
- def __init__(self, embed_rules, reference_extractors=None):
36
- self.embed_rules = embed_rules
31
+ class TagRewriter:
32
+ def __init__(self, rules=None, bulk_rules=None, reference_extractors=None):
33
+ self.rules = rules or {}
34
+ self.bulk_rules = bulk_rules or {}
37
35
  self.reference_extractors = reference_extractors or {}
38
36
 
39
- def replace_tag(self, match):
40
- attrs = extract_attrs(match.group(1))
41
- try:
42
- rule = self.embed_rules[attrs["embedtype"]]
43
- except KeyError:
44
- # silently drop any tags with an unrecognised or missing embedtype attribute
45
- return ""
46
- return rule(attrs)
37
+ def get_opening_tag_regex(self):
38
+ raise NotImplementedError
47
39
 
48
- def __call__(self, html):
49
- return FIND_EMBED_TAG.sub(self.replace_tag, html)
40
+ def get_tag_type_from_attrs(self, attrs):
41
+ raise NotImplementedError
42
+
43
+ def get_tag_replacements(self, tag_type, attrs_list):
44
+ # Note: return an empty list for cases when you don't want any replacements made
45
+ raise NotImplementedError
46
+
47
+ def __call__(self, html: str) -> str:
48
+ matches_by_tag_type, attrs_by_tag_type = self.extract_tags(html)
49
+
50
+ replacements = [
51
+ self.get_tag_replacements(tag_type, attrs_list)
52
+ for tag_type, attrs_list in attrs_by_tag_type.items()
53
+ ]
54
+
55
+ offset = 0
56
+ for match, replacement in zip(
57
+ chain(*matches_by_tag_type.values()), chain(*replacements)
58
+ ):
59
+ html = (
60
+ html[: match.start() + offset]
61
+ + replacement
62
+ + html[match.end() + offset :]
63
+ )
64
+
65
+ offset += len(replacement) - match.end() + match.start()
66
+
67
+ return html
68
+
69
+ def extract_tags(self, html: str) -> Tuple[dict, dict]:
70
+ """Helper method to extract and group HTML tags and their attributes.
71
+
72
+ Returns the full list of regex matches grouped by tag type as well as
73
+ the tag attribute dictionaries grouped by tag type.
74
+ """
75
+ matches_by_tag_type = defaultdict(list)
76
+ attrs_by_tag_type = defaultdict(list)
77
+
78
+ # Regex used to match <tag ...> tags in the HTML.
79
+ re_pattern = self.get_opening_tag_regex()
80
+
81
+ for match in re_pattern.finditer(html):
82
+ attrs = extract_attrs(match.group(1))
83
+ tag_type = self.get_tag_type_from_attrs(attrs)
84
+
85
+ matches_by_tag_type[tag_type].append(match)
86
+ attrs_by_tag_type[tag_type].append(attrs)
87
+
88
+ return matches_by_tag_type, attrs_by_tag_type
89
+
90
+ def convert_rule_to_bulk_rule(self, rule: Callable) -> Callable:
91
+ def bulk_rule(args):
92
+ return list(map(rule, args))
93
+
94
+ return bulk_rule
50
95
 
51
96
  def extract_references(self, html):
52
- for match in FIND_EMBED_TAG.findall(html):
97
+ re_pattern = self.get_opening_tag_regex()
98
+ for match in re_pattern.findall(html):
53
99
  attrs = extract_attrs(match)
54
- if (
55
- "embedtype" not in attrs
56
- or attrs["embedtype"] not in self.reference_extractors
57
- ):
100
+ tag_type = self.get_tag_type_from_attrs(attrs)
101
+
102
+ if tag_type not in self.reference_extractors:
58
103
  continue
59
104
 
60
- yield from self.reference_extractors[attrs["embedtype"]](attrs)
105
+ yield from self.reference_extractors[tag_type](attrs)
106
+
107
+ return []
61
108
 
62
109
 
63
- class LinkRewriter:
110
+ class EmbedRewriter(TagRewriter):
64
111
  """
65
- Rewrites <a linktype="foo"> tags within rich text into the HTML fragment given by the
66
- rule for 'foo'. Each link rule is a function that takes a dict of attributes and
67
- returns the HTML fragment for the opening tag (only).
112
+ Rewrites <embed embedtype="foo" /> tags within rich text into the HTML
113
+ fragment given by the embed rule for 'foo'. Each embed rule is a function
114
+ that takes a dict of attributes and returns the HTML fragment.
68
115
  """
69
116
 
70
- def __init__(self, link_rules, reference_extractors=None):
71
- self.link_rules = link_rules
72
- self.reference_extractors = reference_extractors or {}
117
+ def get_opening_tag_regex(self):
118
+ return FIND_EMBED_TAG
119
+
120
+ def get_tag_type_from_attrs(self, attrs):
121
+ return attrs.get("embedtype")
73
122
 
74
- def replace_tag(self, match):
75
- attrs = extract_attrs(match.group(1))
123
+ def get_tag_replacements(self, tag_type, attrs_list):
76
124
  try:
77
- link_type = attrs["linktype"]
125
+ rule = self.bulk_rules[tag_type]
126
+ except KeyError:
127
+ rule = None
128
+
129
+ if not rule:
130
+ try:
131
+ rule = self.rules[tag_type]
132
+ except KeyError:
133
+ pass
134
+ else:
135
+ rule = self.convert_rule_to_bulk_rule(rule)
136
+
137
+ # Silently drop any tags with an unrecognised or missing embedtype attribute.
138
+ return rule(attrs_list) if rule else [""] * len(attrs_list)
139
+
140
+
141
+ class LinkRewriter(TagRewriter):
142
+ """
143
+ Rewrites <a linktype="foo"> tags within rich text into the HTML fragment
144
+ given by the rule for 'foo'. Each link rule is a function that takes a dict
145
+ of attributes and returns the HTML fragment for the opening tag (only).
146
+ """
147
+
148
+ def get_opening_tag_regex(self):
149
+ return FIND_A_TAG
150
+
151
+ def get_tag_type_from_attrs(self, attrs):
152
+ try:
153
+ return attrs["linktype"]
78
154
  except KeyError:
79
- link_type = None
80
155
  href = attrs.get("href", None)
81
156
  if href:
82
157
  # From href attribute we try to detect only the linktypes that we
83
158
  # currently support (`external` & `email`, `page` has a default handler)
84
159
  # from the link chooser.
85
160
  if href.startswith(("http:", "https:")):
86
- link_type = "external"
161
+ return "external"
87
162
  elif href.startswith("mailto:"):
88
- link_type = "email"
163
+ return "email"
89
164
  elif href.startswith("#"):
90
- link_type = "anchor"
165
+ return "anchor"
91
166
 
92
- if not link_type:
93
- # otherwise return ordinary links without a linktype unchanged
94
- return match.group(0)
167
+ def get_tag_replacements(self, tag_type, attrs_list):
168
+ if not tag_type:
169
+ # We want to leave links without a linktype attribute unchanged,
170
+ # for example <a name="important-anchor">, so we return an empty
171
+ # list here so that no tag replacements are made.
172
+ return []
95
173
 
96
174
  try:
97
- rule = self.link_rules[link_type]
175
+ rule = self.bulk_rules[tag_type]
98
176
  except KeyError:
99
- if link_type in ["email", "external", "anchor"]:
100
- # If no rule is registered for supported types
101
- # return ordinary links without a linktype unchanged
102
- return match.group(0)
103
- # unrecognised link type
104
- return "<a>"
105
-
106
- return rule(attrs)
107
-
108
- def __call__(self, html):
109
- return FIND_A_TAG.sub(self.replace_tag, html)
110
-
111
- def extract_references(self, html):
112
- for match in FIND_A_TAG.findall(html):
113
- attrs = extract_attrs(match)
114
- if (
115
- "linktype" not in attrs
116
- or attrs["linktype"] not in self.reference_extractors
117
- ):
118
- continue
119
-
120
- yield from self.reference_extractors[attrs["linktype"]](attrs)
121
-
122
- return []
177
+ rule = None
178
+
179
+ if not rule:
180
+ try:
181
+ rule = self.rules[tag_type]
182
+ except KeyError:
183
+ if tag_type in ["email", "external", "anchor"]:
184
+ # We also want to leave links with certain known linktype
185
+ # attributes alone even if there are no richtext rules
186
+ # registered for those types, for example
187
+ # <a href="https://wagtail.org">, so we return an empty
188
+ # list here so that no tag replacements are made.
189
+ return []
190
+ else:
191
+ rule = self.convert_rule_to_bulk_rule(rule)
192
+
193
+ # Replace unrecognised link types with an empty link.
194
+ return rule(attrs_list) if rule else ["<a>"] * len(attrs_list)
123
195
 
124
196
 
125
197
  class MultiRuleRewriter:
@@ -276,10 +276,10 @@ class Index:
276
276
  update_method(content_type_pk, indexers)
277
277
 
278
278
  def delete_item(self, item):
279
- item.index_entries.using(self.db_alias).delete()
279
+ item.index_entries.all()._raw_delete(using=self.db_alias)
280
280
 
281
281
  def __str__(self):
282
- return self.nam
282
+ return self.name
283
283
 
284
284
 
285
285
  class MySQLSearchQueryCompiler(BaseSearchQueryCompiler):
@@ -662,7 +662,7 @@ class MySQLSearchBackend(BaseSearchBackend):
662
662
  for connection in connections.all()
663
663
  if connection.vendor == "mysql"
664
664
  ]:
665
- IndexEntry._default_manager.using(connection.alias).delete()
665
+ IndexEntry._default_manager.all()._raw_delete(using=connection.alias)
666
666
 
667
667
  def add_type(self, model):
668
668
  pass # Not needed.
@@ -353,7 +353,7 @@ class Index:
353
353
  update_method(content_type_pk, indexers)
354
354
 
355
355
  def delete_item(self, item):
356
- item.index_entries.using(self.db_alias).delete()
356
+ item.index_entries.all()._raw_delete(using=self.db_alias)
357
357
 
358
358
  def __str__(self):
359
359
  return self.name
@@ -434,7 +434,7 @@ class PostgresSearchQueryCompiler(BaseSearchQueryCompiler):
434
434
  return SearchQuery(lexemes, search_type="raw", config=config)
435
435
 
436
436
  elif isinstance(query, Phrase):
437
- return SearchQuery(query.query_string, search_type="phrase")
437
+ return SearchQuery(query.query_string, search_type="phrase", config=config)
438
438
 
439
439
  elif isinstance(query, Boost):
440
440
  # Not supported
@@ -762,7 +762,7 @@ class PostgresSearchBackend(BaseSearchBackend):
762
762
  for connection in connections.all()
763
763
  if connection.vendor == "postgresql"
764
764
  ]:
765
- IndexEntry._default_manager.using(connection.alias).delete()
765
+ IndexEntry._default_manager.all()._raw_delete(using=connection.alias)
766
766
 
767
767
  def add_type(self, model):
768
768
  pass # Not needed.
@@ -270,7 +270,7 @@ class Index:
270
270
  update_method(content_type_pk, indexers)
271
271
 
272
272
  def delete_item(self, item):
273
- item.index_entries.using(self.db_alias).delete()
273
+ item.index_entries.all()._raw_delete(using=self.db_alias)
274
274
 
275
275
  def __str__(self):
276
276
  return self.name
@@ -685,7 +685,7 @@ class SQLiteSearchBackend(BaseSearchBackend):
685
685
  for connection in connections.all()
686
686
  if connection.vendor == "sqlite"
687
687
  ]:
688
- IndexEntry._default_manager.using(connection.alias).delete()
688
+ IndexEntry._default_manager.all()._raw_delete(using=connection.alias)
689
689
 
690
690
  def add_type(self, model):
691
691
  pass # Not needed.
@@ -52,6 +52,8 @@ class Elasticsearch7Mapping:
52
52
 
53
53
  type_map = {
54
54
  "AutoField": "integer",
55
+ "SmallAutoField": "integer",
56
+ "BigAutoField": "long",
55
57
  "BinaryField": "binary",
56
58
  "BooleanField": "boolean",
57
59
  "CharField": "string",
@@ -69,10 +71,12 @@ class Elasticsearch7Mapping:
69
71
  "NullBooleanField": "boolean",
70
72
  "PositiveIntegerField": "integer",
71
73
  "PositiveSmallIntegerField": "integer",
74
+ "PositiveBigIntegerField": "long",
72
75
  "SlugField": "string",
73
76
  "SmallIntegerField": "integer",
74
77
  "TextField": "string",
75
78
  "TimeField": "date",
79
+ "URLField": "string",
76
80
  }
77
81
 
78
82
  keyword_type = "keyword"
@@ -8,7 +8,7 @@ msgid ""
8
8
  msgstr ""
9
9
  "Project-Id-Version: PACKAGE VERSION\n"
10
10
  "Report-Msgid-Bugs-To: \n"
11
- "POT-Creation-Date: 2024-01-24 13:51+0000\n"
11
+ "POT-Creation-Date: 2024-04-18 17:28+0100\n"
12
12
  "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13
13
  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14
14
  "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -22,10 +22,10 @@ msgstr ""
22
22
  msgid "Wagtail search"
23
23
  msgstr ""
24
24
 
25
- #: models.py:76
25
+ #: models.py:65
26
26
  msgid "index entry"
27
27
  msgstr ""
28
28
 
29
- #: models.py:77
29
+ #: models.py:66
30
30
  msgid "index entries"
31
31
  msgstr ""
@@ -4,6 +4,7 @@ from django.db import connection
4
4
  from django.test import TestCase
5
5
  from django.test.utils import override_settings
6
6
 
7
+ from wagtail.search.query import Phrase
7
8
  from wagtail.search.tests.test_backends import BackendTests
8
9
  from wagtail.test.search import models
9
10
 
@@ -172,3 +173,52 @@ class TestPostgresSearchBackend(BackendTests, TestCase):
172
173
  [r.title for r in results],
173
174
  ["JavaScript: The good parts", "JavaScript: The Definitive Guide"],
174
175
  )
176
+
177
+
178
+ @unittest.skipUnless(
179
+ connection.vendor == "postgresql", "The current database is not PostgreSQL"
180
+ )
181
+ @override_settings(
182
+ WAGTAILSEARCH_BACKENDS={
183
+ "default": {
184
+ "BACKEND": "wagtail.search.backends.database.postgres.postgres",
185
+ "SEARCH_CONFIG": "dutch",
186
+ }
187
+ }
188
+ )
189
+ class TestPostgresLanguageTextSearch(TestCase):
190
+ backend_path = "wagtail.search.backends.database.postgres.postgres"
191
+
192
+ def setUp(self):
193
+ # get search backend by backend_path
194
+ BackendTests.setUp(self)
195
+
196
+ book = models.Book.objects.create(
197
+ title="Nu is beter dan nooit",
198
+ publication_date="1999-05-01",
199
+ number_of_pages=333,
200
+ )
201
+ self.backend.add(book)
202
+ self.book = book
203
+
204
+ def test_search_language_plain_text(self):
205
+ results = self.backend.search("Nu is beter dan nooit", models.Book)
206
+ self.assertEqual(list(results), [self.book])
207
+
208
+ results = self.backend.search("is beter", models.Book)
209
+ self.assertEqual(list(results), [self.book])
210
+
211
+ # search deals even with variations
212
+ results = self.backend.search("zijn beter", models.Book)
213
+ self.assertEqual(list(results), [self.book])
214
+
215
+ # search deals even when there are minor typos
216
+ results = self.backend.search("zij beter dan", models.Book)
217
+ self.assertEqual(list(results), [self.book])
218
+
219
+ def test_search_language_phrase_text(self):
220
+ results = self.backend.search(Phrase("Nu is beter"), models.Book)
221
+ self.assertEqual(list(results), [self.book])
222
+
223
+ results = self.backend.search(Phrase("Nu zijn beter"), models.Book)
224
+ self.assertEqual(list(results), [self.book])
@@ -8,7 +8,7 @@ msgid ""
8
8
  msgstr ""
9
9
  "Project-Id-Version: PACKAGE VERSION\n"
10
10
  "Report-Msgid-Bugs-To: \n"
11
- "POT-Creation-Date: 2024-01-24 13:51+0000\n"
11
+ "POT-Creation-Date: 2024-04-18 17:28+0100\n"
12
12
  "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13
13
  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14
14
  "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -50,33 +50,33 @@ msgstr ""
50
50
  msgid "Default"
51
51
  msgstr ""
52
52
 
53
- #: views.py:33
53
+ #: views.py:36
54
54
  msgid "Add site"
55
55
  msgstr ""
56
56
 
57
- #: views.py:34
57
+ #: views.py:37
58
58
  #, python-format
59
59
  msgid "Site '%(object)s' created."
60
60
  msgstr ""
61
61
 
62
- #: views.py:35 views.py:40
62
+ #: views.py:38 views.py:43
63
63
  msgid "The site could not be saved due to errors."
64
64
  msgstr ""
65
65
 
66
- #: views.py:39
66
+ #: views.py:42
67
67
  #, python-format
68
68
  msgid "Site '%(object)s' updated."
69
69
  msgstr ""
70
70
 
71
- #: views.py:41 views.py:47
71
+ #: views.py:44 views.py:50
72
72
  msgid "Delete site"
73
73
  msgstr ""
74
74
 
75
- #: views.py:46
75
+ #: views.py:49
76
76
  #, python-format
77
77
  msgid "Site '%(object)s' deleted."
78
78
  msgstr ""
79
79
 
80
- #: views.py:48
80
+ #: views.py:51
81
81
  msgid "Are you sure you want to delete this site?"
82
82
  msgstr ""
@@ -17,8 +17,8 @@ msgstr ""
17
17
  "Content-Type: text/plain; charset=UTF-8\n"
18
18
  "Content-Transfer-Encoding: 8bit\n"
19
19
  "Language: he_IL\n"
20
- "Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % "
21
- "1 == 0) ? 1: (n % 10 == 0 && n % 1 == 0 && n > 10) ? 2 : 3;\n"
20
+ "Plural-Forms: nplurals=3; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % "
21
+ "1 == 0) ? 1: 2;\n"
22
22
 
23
23
  msgid "Choose a root page"
24
24
  msgstr "בחרו עמוד מקור"
@@ -4,13 +4,14 @@
4
4
  #
5
5
  # Translators:
6
6
  # Dan Braghis, 2014-2016,2019
7
+ # Madalin Popa, 2024
7
8
  msgid ""
8
9
  msgstr ""
9
10
  "Project-Id-Version: Wagtail\n"
10
11
  "Report-Msgid-Bugs-To: \n"
11
12
  "POT-Creation-Date: 2024-01-24 13:51+0000\n"
12
13
  "PO-Revision-Date: 2014-10-03 15:24+0000\n"
13
- "Last-Translator: Dan Braghis, 2014-2016,2019\n"
14
+ "Last-Translator: Madalin Popa, 2024\n"
14
15
  "Language-Team: Romanian (http://app.transifex.com/torchbox/wagtail/language/"
15
16
  "ro/)\n"
16
17
  "MIME-Version: 1.0\n"
@@ -21,7 +22,7 @@ msgstr ""
21
22
  "2:1));\n"
22
23
 
23
24
  msgid "Wagtail sites"
24
- msgstr "Situri Wagtail"
25
+ msgstr "Site-uri Wagtail"
25
26
 
26
27
  msgid "Choose a root page"
27
28
  msgstr "Alege o pagină de bază"