wagtail 7.2.1__py3-none-any.whl → 7.3rc1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (312) hide show
  1. wagtail/__init__.py +1 -1
  2. wagtail/actions/copy_for_translation.py +4 -2
  3. wagtail/admin/action_menu.py +4 -1
  4. wagtail/admin/api/actions/convert_alias.py +2 -2
  5. wagtail/admin/api/actions/copy.py +2 -2
  6. wagtail/admin/api/actions/copy_for_translation.py +2 -2
  7. wagtail/admin/api/actions/create_alias.py +2 -2
  8. wagtail/admin/api/actions/delete.py +1 -1
  9. wagtail/admin/api/actions/move.py +1 -1
  10. wagtail/admin/api/actions/publish.py +2 -2
  11. wagtail/admin/api/actions/revert_to_page_revision.py +2 -2
  12. wagtail/admin/api/actions/unpublish.py +1 -1
  13. wagtail/admin/api/filters.py +2 -2
  14. wagtail/admin/compare.py +22 -0
  15. wagtail/admin/forms/account.py +52 -1
  16. wagtail/admin/forms/comments.py +53 -0
  17. wagtail/admin/forms/models.py +36 -0
  18. wagtail/admin/forms/pages.py +7 -0
  19. wagtail/admin/forms/workflows.py +5 -2
  20. wagtail/admin/icons.py +4 -3
  21. wagtail/admin/locale/ar/LC_MESSAGES/django.mo +0 -0
  22. wagtail/admin/locale/ar/LC_MESSAGES/django.po +35 -0
  23. wagtail/admin/locale/en/LC_MESSAGES/django.po +262 -234
  24. wagtail/admin/locale/en/LC_MESSAGES/djangojs.po +72 -43
  25. wagtail/admin/locale/it/LC_MESSAGES/django.mo +0 -0
  26. wagtail/admin/locale/it/LC_MESSAGES/django.po +566 -6
  27. wagtail/admin/locale/it/LC_MESSAGES/djangojs.mo +0 -0
  28. wagtail/admin/locale/it/LC_MESSAGES/djangojs.po +40 -2
  29. wagtail/admin/locale/nl/LC_MESSAGES/django.mo +0 -0
  30. wagtail/admin/locale/nl/LC_MESSAGES/django.po +2 -2
  31. wagtail/admin/locale/sv/LC_MESSAGES/django.mo +0 -0
  32. wagtail/admin/locale/sv/LC_MESSAGES/django.po +330 -15
  33. wagtail/admin/locale/sv/LC_MESSAGES/djangojs.mo +0 -0
  34. wagtail/admin/locale/sv/LC_MESSAGES/djangojs.po +3 -2
  35. wagtail/admin/panels/comment_panel.py +1 -51
  36. wagtail/admin/panels/title_field_panel.py +3 -1
  37. wagtail/admin/static/wagtailadmin/css/core.css +1 -1
  38. wagtail/admin/static/wagtailadmin/js/bulk-actions.js +1 -1
  39. wagtail/admin/static/wagtailadmin/js/comments.js +1 -1
  40. wagtail/admin/static/wagtailadmin/js/core.js +1 -1
  41. wagtail/admin/static/wagtailadmin/js/core.js.LICENSE.txt +1 -1
  42. wagtail/admin/static/wagtailadmin/js/draftail.js +1 -1
  43. wagtail/admin/static/wagtailadmin/js/privacy-switch.js +1 -1
  44. wagtail/admin/static/wagtailadmin/js/sidebar.js +1 -1
  45. wagtail/admin/static/wagtailadmin/js/telepath/blocks.js +1 -1
  46. wagtail/admin/static/wagtailadmin/js/userbar.js +1 -1
  47. wagtail/admin/static/wagtailadmin/js/userbar.js.LICENSE.txt +1 -1
  48. wagtail/admin/static/wagtailadmin/js/vendor.js +1 -1
  49. wagtail/admin/static/wagtailadmin/js/wagtailadmin.js +1 -1
  50. wagtail/admin/templates/wagtailadmin/base.html +1 -1
  51. wagtail/admin/templates/wagtailadmin/generic/edit_partials.html +100 -0
  52. wagtail/admin/templates/wagtailadmin/generic/form.html +26 -5
  53. wagtail/admin/templates/wagtailadmin/generic/includes/_loaded_revision_inputs.html +3 -0
  54. wagtail/admin/templates/wagtailadmin/generic/listing_results.html +1 -17
  55. wagtail/admin/templates/wagtailadmin/pages/create.html +14 -4
  56. wagtail/admin/templates/wagtailadmin/pages/edit.html +16 -3
  57. wagtail/admin/templates/wagtailadmin/pages/edit_partials.html +31 -0
  58. wagtail/admin/templates/wagtailadmin/pages/index_results.html +1 -9
  59. wagtail/admin/templates/wagtailadmin/shared/autosave/indicator.html +36 -0
  60. wagtail/admin/templates/wagtailadmin/shared/breadcrumbs.html +1 -1
  61. wagtail/admin/templates/wagtailadmin/shared/editing_sessions/list.html +2 -2
  62. wagtail/admin/templates/wagtailadmin/shared/editing_sessions/module.html +19 -3
  63. wagtail/admin/templates/wagtailadmin/shared/headers/_actions.html +5 -0
  64. wagtail/admin/templates/wagtailadmin/shared/headers/_history_icon_link.html +2 -2
  65. wagtail/admin/templates/wagtailadmin/shared/headers/slim_header.html +8 -9
  66. wagtail/admin/templates/wagtailadmin/shared/listing/filter_partials.html +19 -0
  67. wagtail/admin/templates/wagtailadmin/shared/side_panel_toggle.html +3 -3
  68. wagtail/admin/templates/wagtailadmin/shared/side_panels/includes/side_panel.html +3 -0
  69. wagtail/admin/templates/wagtailadmin/shared/side_panels/includes/status/privacy.html +18 -6
  70. wagtail/admin/templates/wagtailadmin/shared/side_panels/includes/status/usage.html +11 -4
  71. wagtail/admin/templates/wagtailadmin/shared/side_panels/includes/status/workflow.html +22 -1
  72. wagtail/admin/templates/wagtailadmin/shared/side_panels/preview.html +1 -0
  73. wagtail/admin/templates/wagtailadmin/shared/side_panels.html +1 -2
  74. wagtail/admin/templates/wagtailadmin/shared/unsaved_changes_warning.html +20 -20
  75. wagtail/admin/templates/wagtailadmin/userbar/base.html +2 -0
  76. wagtail/admin/templates/wagtailadmin/userbar/item_accessibility.html +1 -1
  77. wagtail/admin/templatetags/wagtailadmin_tags.py +6 -14
  78. wagtail/admin/tests/pages/test_create_page.py +298 -1
  79. wagtail/admin/tests/pages/test_custom_listing.py +48 -2
  80. wagtail/admin/tests/pages/test_edit_page.py +721 -7
  81. wagtail/admin/tests/pages/test_explorer_view.py +9 -5
  82. wagtail/admin/tests/pages/test_page_search.py +15 -0
  83. wagtail/admin/tests/pages/test_revisions.py +4 -0
  84. wagtail/admin/tests/test_account_management.py +88 -2
  85. wagtail/admin/tests/test_collections_views.py +15 -15
  86. wagtail/admin/tests/test_compare.py +34 -0
  87. wagtail/admin/tests/test_editing_sessions.py +230 -8
  88. wagtail/admin/tests/test_forms.py +192 -1
  89. wagtail/admin/tests/test_icon_sprite.py +22 -2
  90. wagtail/admin/tests/test_page_chooser.py +13 -13
  91. wagtail/admin/tests/test_reports_views.py +4 -1
  92. wagtail/admin/tests/test_userbar.py +69 -5
  93. wagtail/admin/tests/test_views_generic.py +5 -5
  94. wagtail/admin/tests/test_workflows.py +14 -12
  95. wagtail/admin/tests/viewsets/test_model_viewset.py +13 -0
  96. wagtail/admin/ui/autosave.py +5 -0
  97. wagtail/admin/ui/editing_sessions.py +3 -0
  98. wagtail/admin/ui/side_panels.py +19 -20
  99. wagtail/admin/userbar.py +48 -27
  100. wagtail/admin/views/bulk_action/dispatcher.py +2 -2
  101. wagtail/admin/views/chooser.py +6 -6
  102. wagtail/admin/views/editing_sessions.py +20 -7
  103. wagtail/admin/views/generic/__init__.py +1 -0
  104. wagtail/admin/views/generic/chooser.py +5 -5
  105. wagtail/admin/views/generic/mixins.py +143 -26
  106. wagtail/admin/views/generic/models.py +157 -27
  107. wagtail/admin/views/generic/ordering.py +1 -1
  108. wagtail/admin/views/generic/preview.py +4 -4
  109. wagtail/admin/views/home.py +3 -1
  110. wagtail/admin/views/pages/create.py +77 -29
  111. wagtail/admin/views/pages/edit.py +160 -34
  112. wagtail/admin/views/pages/preview.py +4 -4
  113. wagtail/admin/views/pages/revisions.py +3 -0
  114. wagtail/admin/views/pages/search.py +4 -4
  115. wagtail/admin/views/pages/usage.py +2 -2
  116. wagtail/admin/views/tags.py +2 -2
  117. wagtail/admin/views/workflows.py +73 -54
  118. wagtail/admin/viewsets/model.py +34 -8
  119. wagtail/admin/widgets/button.py +11 -4
  120. wagtail/admin/widgets/chooser.py +6 -4
  121. wagtail/admin/widgets/slug.py +1 -1
  122. wagtail/api/v2/filters.py +27 -21
  123. wagtail/api/v2/pagination.py +4 -4
  124. wagtail/api/v2/views.py +7 -7
  125. wagtail/blocks/list_block.py +0 -8
  126. wagtail/blocks/migrations/migrate_operation.py +8 -1
  127. wagtail/blocks/stream_block.py +2 -10
  128. wagtail/blocks/struct_block.py +192 -12
  129. wagtail/compat.py +2 -2
  130. wagtail/contrib/forms/locale/en/LC_MESSAGES/django.po +1 -1
  131. wagtail/contrib/forms/locale/it/LC_MESSAGES/django.mo +0 -0
  132. wagtail/contrib/forms/locale/it/LC_MESSAGES/django.po +40 -2
  133. wagtail/contrib/frontend_cache/backends/azure.py +6 -6
  134. wagtail/contrib/frontend_cache/backends/cloudfront.py +2 -2
  135. wagtail/contrib/frontend_cache/utils.py +1 -1
  136. wagtail/contrib/redirects/forms.py +1 -1
  137. wagtail/contrib/redirects/locale/en/LC_MESSAGES/django.po +14 -14
  138. wagtail/contrib/redirects/locale/it/LC_MESSAGES/django.mo +0 -0
  139. wagtail/contrib/redirects/locale/it/LC_MESSAGES/django.po +15 -2
  140. wagtail/contrib/redirects/locale/sv/LC_MESSAGES/django.mo +0 -0
  141. wagtail/contrib/redirects/locale/sv/LC_MESSAGES/django.po +16 -3
  142. wagtail/contrib/redirects/middleware.py +11 -7
  143. wagtail/contrib/redirects/models.py +17 -1
  144. wagtail/contrib/redirects/tests/test_import_admin_views.py +3 -3
  145. wagtail/contrib/redirects/tests/test_redirects.py +56 -8
  146. wagtail/contrib/search_promotions/locale/en/LC_MESSAGES/django.po +6 -6
  147. wagtail/contrib/search_promotions/locale/it/LC_MESSAGES/django.mo +0 -0
  148. wagtail/contrib/search_promotions/locale/it/LC_MESSAGES/django.po +43 -2
  149. wagtail/contrib/search_promotions/tests.py +1 -1
  150. wagtail/contrib/search_promotions/views/settings.py +24 -25
  151. wagtail/contrib/settings/jinja2tags.py +2 -2
  152. wagtail/contrib/settings/locale/en/LC_MESSAGES/django.po +2 -2
  153. wagtail/contrib/settings/locale/it/LC_MESSAGES/django.mo +0 -0
  154. wagtail/contrib/settings/locale/it/LC_MESSAGES/django.po +6 -2
  155. wagtail/contrib/settings/locale/sv/LC_MESSAGES/django.mo +0 -0
  156. wagtail/contrib/settings/locale/sv/LC_MESSAGES/django.po +3 -2
  157. wagtail/contrib/settings/tests/generic/test_admin.py +118 -2
  158. wagtail/contrib/settings/tests/site_specific/test_admin.py +78 -15
  159. wagtail/contrib/settings/tests/site_specific/test_model.py +2 -2
  160. wagtail/contrib/settings/views.py +7 -0
  161. wagtail/contrib/simple_translation/locale/en/LC_MESSAGES/django.po +1 -1
  162. wagtail/contrib/simple_translation/locale/it/LC_MESSAGES/django.mo +0 -0
  163. wagtail/contrib/simple_translation/locale/it/LC_MESSAGES/django.po +5 -2
  164. wagtail/contrib/styleguide/locale/en/LC_MESSAGES/django.po +7 -7
  165. wagtail/contrib/styleguide/tests.py +2 -0
  166. wagtail/contrib/styleguide/views.py +4 -5
  167. wagtail/contrib/table_block/locale/ar/LC_MESSAGES/django.mo +0 -0
  168. wagtail/contrib/table_block/locale/ar/LC_MESSAGES/django.po +5 -1
  169. wagtail/contrib/table_block/locale/en/LC_MESSAGES/django.po +1 -1
  170. wagtail/contrib/table_block/locale/it/LC_MESSAGES/django.mo +0 -0
  171. wagtail/contrib/table_block/locale/it/LC_MESSAGES/django.po +5 -2
  172. wagtail/contrib/typed_table_block/blocks.py +37 -0
  173. wagtail/contrib/typed_table_block/locale/en/LC_MESSAGES/django.po +10 -10
  174. wagtail/contrib/typed_table_block/locale/sv/LC_MESSAGES/django.mo +0 -0
  175. wagtail/contrib/typed_table_block/locale/sv/LC_MESSAGES/django.po +4 -3
  176. wagtail/contrib/typed_table_block/tests.py +108 -0
  177. wagtail/coreutils.py +4 -4
  178. wagtail/documents/__init__.py +4 -4
  179. wagtail/documents/locale/en/LC_MESSAGES/django.po +15 -15
  180. wagtail/documents/locale/it/LC_MESSAGES/django.mo +0 -0
  181. wagtail/documents/locale/it/LC_MESSAGES/django.po +32 -2
  182. wagtail/documents/locale/sv/LC_MESSAGES/django.mo +0 -0
  183. wagtail/documents/locale/sv/LC_MESSAGES/django.po +32 -4
  184. wagtail/documents/models.py +1 -1
  185. wagtail/documents/tests/test_admin_views.py +19 -4
  186. wagtail/documents/tests/test_search.py +0 -2
  187. wagtail/documents/tests/test_views.py +6 -4
  188. wagtail/documents/views/bulk_actions/add_tags.py +1 -1
  189. wagtail/embeds/finders/facebook.py +3 -3
  190. wagtail/embeds/finders/instagram.py +3 -3
  191. wagtail/embeds/finders/oembed.py +7 -2
  192. wagtail/embeds/locale/en/LC_MESSAGES/django.po +1 -1
  193. wagtail/embeds/oembed_providers.py +8 -0
  194. wagtail/embeds/tests/test_embeds.py +35 -0
  195. wagtail/images/__init__.py +4 -4
  196. wagtail/images/admin_urls.py +3 -3
  197. wagtail/images/blocks.py +1 -1
  198. wagtail/images/formats.py +2 -2
  199. wagtail/images/image_operations.py +2 -2
  200. wagtail/images/locale/en/LC_MESSAGES/django.po +44 -40
  201. wagtail/images/locale/it/LC_MESSAGES/django.mo +0 -0
  202. wagtail/images/locale/it/LC_MESSAGES/django.po +50 -4
  203. wagtail/images/locale/nl/LC_MESSAGES/django.mo +0 -0
  204. wagtail/images/locale/nl/LC_MESSAGES/django.po +3 -3
  205. wagtail/images/locale/sv/LC_MESSAGES/django.mo +0 -0
  206. wagtail/images/locale/sv/LC_MESSAGES/django.po +49 -6
  207. wagtail/images/models.py +11 -10
  208. wagtail/images/templates/wagtailimages/images/index_results.html +1 -1
  209. wagtail/images/templates/wagtailimages/images/url_generator.html +17 -38
  210. wagtail/images/templates/wagtailimages/images/url_generator_output.html +28 -0
  211. wagtail/images/templatetags/wagtailimages_tags.py +4 -4
  212. wagtail/images/tests/test_admin_views.py +432 -59
  213. wagtail/images/tests/test_image_operations.py +2 -2
  214. wagtail/images/tests/test_models.py +0 -2
  215. wagtail/images/views/bulk_actions/add_tags.py +1 -1
  216. wagtail/images/views/images.py +72 -39
  217. wagtail/locale/en/LC_MESSAGES/django.po +103 -105
  218. wagtail/locale/it/LC_MESSAGES/django.mo +0 -0
  219. wagtail/locale/it/LC_MESSAGES/django.po +105 -2
  220. wagtail/locale/sv/LC_MESSAGES/django.mo +0 -0
  221. wagtail/locale/sv/LC_MESSAGES/django.po +95 -12
  222. wagtail/locales/locale/en/LC_MESSAGES/django.po +1 -1
  223. wagtail/locales/locale/it/LC_MESSAGES/django.mo +0 -0
  224. wagtail/locales/locale/it/LC_MESSAGES/django.po +13 -2
  225. wagtail/locales/locale/sv/LC_MESSAGES/django.mo +0 -0
  226. wagtail/locales/locale/sv/LC_MESSAGES/django.po +4 -3
  227. wagtail/locales/tests.py +5 -5
  228. wagtail/models/i18n.py +4 -6
  229. wagtail/models/media.py +18 -0
  230. wagtail/models/pages.py +65 -11
  231. wagtail/models/reference_index.py +15 -0
  232. wagtail/models/revisions.py +40 -12
  233. wagtail/models/workflows.py +0 -3
  234. wagtail/permission_policies/base.py +2 -2
  235. wagtail/permission_policies/collections.py +2 -2
  236. wagtail/project_template/requirements.txt +2 -2
  237. wagtail/search/locale/en/LC_MESSAGES/django.po +1 -1
  238. wagtail/signal_handlers.py +1 -0
  239. wagtail/sites/locale/en/LC_MESSAGES/django.po +1 -1
  240. wagtail/sites/locale/sv/LC_MESSAGES/django.mo +0 -0
  241. wagtail/sites/locale/sv/LC_MESSAGES/django.po +3 -2
  242. wagtail/sites/tests.py +7 -7
  243. wagtail/snippets/action_menu.py +2 -1
  244. wagtail/snippets/locale/en/LC_MESSAGES/django.po +23 -13
  245. wagtail/snippets/locale/sv/LC_MESSAGES/django.mo +0 -0
  246. wagtail/snippets/locale/sv/LC_MESSAGES/django.po +12 -1
  247. wagtail/snippets/tests/test_chooser_block.py +197 -0
  248. wagtail/snippets/tests/test_chooser_panel.py +149 -0
  249. wagtail/snippets/tests/test_chooser_views.py +375 -0
  250. wagtail/snippets/tests/test_chooser_widget.py +22 -0
  251. wagtail/snippets/tests/test_compare_revisions_view.py +167 -0
  252. wagtail/snippets/tests/test_copy_view.py +38 -0
  253. wagtail/snippets/tests/test_create_view.py +1159 -0
  254. wagtail/snippets/tests/test_delete_view.py +225 -0
  255. wagtail/snippets/tests/test_edit_view.py +2882 -0
  256. wagtail/snippets/tests/test_history_view.py +182 -0
  257. wagtail/snippets/tests/test_index_view.py +105 -0
  258. wagtail/snippets/tests/test_list_view.py +786 -0
  259. wagtail/snippets/tests/test_locking.py +28 -0
  260. wagtail/snippets/tests/test_permissions.py +167 -0
  261. wagtail/snippets/tests/test_preview.py +3 -3
  262. wagtail/snippets/tests/test_revert_view.py +343 -0
  263. wagtail/snippets/tests/test_snippet_models.py +112 -0
  264. wagtail/snippets/tests/test_unpublish_view.py +228 -0
  265. wagtail/snippets/tests/test_unschedule_view.py +151 -0
  266. wagtail/snippets/tests/test_viewset.py +38 -5
  267. wagtail/snippets/views/snippets.py +78 -30
  268. wagtail/snippets/widgets.py +2 -2
  269. wagtail/templatetags/wagtailcore_tags.py +2 -2
  270. wagtail/test/dummy_external_storage.py +1 -1
  271. wagtail/test/testapp/fixtures/test.json +22 -0
  272. wagtail/test/testapp/fixtures/test_empty.json +2 -0
  273. wagtail/test/testapp/fixtures/test_explorable_pages.json +10 -0
  274. wagtail/test/testapp/fixtures/test_specific.json +9 -0
  275. wagtail/test/testapp/migrations/0059_nopromotepage.py +25 -0
  276. wagtail/test/testapp/models.py +7 -0
  277. wagtail/test/testapp/views.py +5 -0
  278. wagtail/test/utils/page_tests.py +7 -7
  279. wagtail/test/utils/wagtail_factories/builder.py +2 -2
  280. wagtail/tests/streamfield_migrations/test_migrations.py +35 -0
  281. wagtail/tests/test_blocks.py +321 -61
  282. wagtail/tests/test_collection_model.py +12 -0
  283. wagtail/tests/test_page_model.py +190 -1
  284. wagtail/tests/test_page_privacy.py +5 -0
  285. wagtail/tests/test_reference_index.py +42 -0
  286. wagtail/tests/test_revision_model.py +118 -1
  287. wagtail/tests/test_utils.py +2 -2
  288. wagtail/users/locale/en/LC_MESSAGES/django.po +14 -14
  289. wagtail/users/locale/id_ID/LC_MESSAGES/django.mo +0 -0
  290. wagtail/users/locale/id_ID/LC_MESSAGES/django.po +6 -2
  291. wagtail/users/locale/it/LC_MESSAGES/django.mo +0 -0
  292. wagtail/users/locale/it/LC_MESSAGES/django.po +14 -2
  293. wagtail/users/locale/sv/LC_MESSAGES/django.mo +0 -0
  294. wagtail/users/locale/sv/LC_MESSAGES/django.po +48 -17
  295. wagtail/users/tests/test_admin_views.py +39 -25
  296. wagtail/users/utils.py +4 -1
  297. wagtail/users/views/groups.py +19 -5
  298. wagtail/users/wagtail_hooks.py +1 -1
  299. wagtail/utils/loading.py +2 -2
  300. wagtail-7.3rc1.data/data/AGENTS.md +21 -0
  301. wagtail-7.3rc1.data/data/CHANGELOG.txt +4941 -0
  302. wagtail-7.3rc1.data/data/CODE_OF_CONDUCT.md +71 -0
  303. wagtail-7.3rc1.data/data/CONTRIBUTORS.md +999 -0
  304. wagtail-7.3rc1.data/data/SPONSORS.md +55 -0
  305. {wagtail-7.2.1.dist-info → wagtail-7.3rc1.dist-info}/METADATA +6 -6
  306. {wagtail-7.2.1.dist-info → wagtail-7.3rc1.dist-info}/RECORD +310 -280
  307. {wagtail-7.2.1.dist-info → wagtail-7.3rc1.dist-info}/WHEEL +1 -1
  308. wagtail/images/static/wagtailimages/js/image-url-generator.js +0 -1
  309. wagtail/snippets/tests/test_snippets.py +0 -6377
  310. {wagtail-7.2.1.dist-info → wagtail-7.3rc1.dist-info}/entry_points.txt +0 -0
  311. {wagtail-7.2.1.dist-info → wagtail-7.3rc1.dist-info}/licenses/LICENSE +0 -0
  312. {wagtail-7.2.1.dist-info → wagtail-7.3rc1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,375 @@
1
+ import json
2
+
3
+ from django.contrib.admin.utils import quote
4
+ from django.test import TestCase, TransactionTestCase
5
+ from django.test.utils import override_settings
6
+ from django.urls import reverse
7
+
8
+ from wagtail.models import Locale
9
+ from wagtail.test.snippets.models import (
10
+ NonAutocompleteSearchableSnippet,
11
+ SearchableSnippet,
12
+ TranslatableSnippet,
13
+ )
14
+ from wagtail.test.testapp.models import (
15
+ Advert,
16
+ AdvertWithCustomPrimaryKey,
17
+ AdvertWithCustomUUIDPrimaryKey,
18
+ DraftStateModel,
19
+ )
20
+ from wagtail.test.utils import WagtailTestUtils
21
+
22
+
23
+ class TestSnippetChoose(WagtailTestUtils, TestCase):
24
+ fixtures = ["test.json"]
25
+
26
+ def setUp(self):
27
+ self.login()
28
+ self.url_args = ["tests", "advert"]
29
+
30
+ def get(self, params=None):
31
+ app_label, model_name = self.url_args
32
+ return self.client.get(
33
+ reverse(f"wagtailsnippetchoosers_{app_label}_{model_name}:choose"),
34
+ params or {},
35
+ )
36
+
37
+ def test_simple(self):
38
+ response = self.get()
39
+ self.assertTemplateUsed(response, "wagtailadmin/generic/chooser/chooser.html")
40
+
41
+ # Check locale filter doesn't exist normally
42
+ self.assertNotIn(
43
+ '<select data-chooser-modal-search-filter name="lang">',
44
+ response.json()["html"],
45
+ )
46
+
47
+ def test_no_results(self):
48
+ Advert.objects.all().delete()
49
+ response = self.get()
50
+ self.assertTemplateUsed(response, "wagtailadmin/generic/chooser/chooser.html")
51
+ response_html = response.json()["html"]
52
+ self.assertIn('href="/admin/snippets/tests/advert/add/"', response_html)
53
+
54
+ def test_ordering(self):
55
+ """
56
+ Listing should be ordered by PK if no ordering has been set on the model
57
+ """
58
+ Advert.objects.all().delete()
59
+ for i in range(10, 0, -1):
60
+ Advert.objects.create(pk=i, text="advert %d" % i)
61
+ response = self.get()
62
+ self.assertEqual(response.status_code, 200)
63
+ self.assertEqual(response.context["results"][0].text, "advert 1")
64
+
65
+ def test_simple_pagination(self):
66
+ # page numbers in range should be accepted
67
+ response = self.get({"p": 1})
68
+ self.assertEqual(response.status_code, 200)
69
+ self.assertTemplateUsed(response, "wagtailadmin/generic/chooser/chooser.html")
70
+ # page numbers out of range should return 404
71
+ response = self.get({"p": 9999})
72
+ self.assertEqual(response.status_code, 404)
73
+
74
+ def test_not_searchable(self):
75
+ # filter_form should not have a search field
76
+ self.assertFalse(self.get().context["filter_form"].fields.get("q"))
77
+
78
+ @override_settings(WAGTAIL_I18N_ENABLED=False)
79
+ def test_locale_filter_requires_i18n_enabled(self):
80
+ self.url_args = ["snippetstests", "translatablesnippet"]
81
+ fr_locale = Locale.objects.create(language_code="fr")
82
+
83
+ TranslatableSnippet.objects.create(text="English snippet")
84
+ TranslatableSnippet.objects.create(text="French snippet", locale=fr_locale)
85
+
86
+ response = self.get()
87
+
88
+ # Check the filter is omitted
89
+ response_html = response.json()["html"]
90
+ self.assertNotIn("data-chooser-modal-search-filter", response_html)
91
+ self.assertNotIn('name="locale"', response_html)
92
+
93
+ @override_settings(WAGTAIL_I18N_ENABLED=True)
94
+ def test_filter_by_locale(self):
95
+ self.url_args = ["snippetstests", "translatablesnippet"]
96
+ fr_locale = Locale.objects.create(language_code="fr")
97
+
98
+ TranslatableSnippet.objects.create(text="English snippet")
99
+ TranslatableSnippet.objects.create(text="French snippet", locale=fr_locale)
100
+
101
+ response = self.get()
102
+
103
+ # Check the filter is added
104
+ response_html = response.json()["html"]
105
+ self.assertIn("data-chooser-modal-search-filter", response_html)
106
+ self.assertIn('name="locale"', response_html)
107
+
108
+ # Check both snippets are shown
109
+ self.assertEqual(len(response.context["results"]), 2)
110
+ self.assertEqual(response.context["results"][0].text, "English snippet")
111
+ self.assertEqual(response.context["results"][1].text, "French snippet")
112
+
113
+ # Now test with a locale selected
114
+ response = self.get({"locale": "en"})
115
+
116
+ self.assertEqual(len(response.context["results"]), 1)
117
+ self.assertEqual(response.context["results"][0].text, "English snippet")
118
+
119
+
120
+ class TestSnippetChooseResults(WagtailTestUtils, TestCase):
121
+ fixtures = ["test.json"]
122
+
123
+ def setUp(self):
124
+ self.login()
125
+
126
+ def get(self, params=None):
127
+ return self.client.get(
128
+ reverse("wagtailsnippetchoosers_tests_advert:choose_results"), params or {}
129
+ )
130
+
131
+ def test_simple(self):
132
+ response = self.get()
133
+ self.assertTemplateUsed(response, "wagtailsnippets/chooser/results.html")
134
+
135
+ def test_no_results(self):
136
+ Advert.objects.all().delete()
137
+ response = self.get()
138
+ self.assertTemplateUsed(response, "wagtailsnippets/chooser/results.html")
139
+ self.assertContains(
140
+ response,
141
+ 'href="/admin/snippets/tests/advert/add/"',
142
+ )
143
+
144
+
145
+ class TestSnippetChooseStatus(WagtailTestUtils, TestCase):
146
+ def setUp(self):
147
+ self.login()
148
+
149
+ @classmethod
150
+ def setUpTestData(cls):
151
+ cls.draft = DraftStateModel.objects.create(text="foo", live=False)
152
+ cls.live = DraftStateModel.objects.create(text="bar", live=True)
153
+ cls.live_draft = DraftStateModel.objects.create(text="baz", live=True)
154
+ cls.live_draft.save_revision()
155
+
156
+ def get(self, view_name, params=None):
157
+ return self.client.get(
158
+ reverse(f"wagtailsnippetchoosers_tests_draftstatemodel:{view_name}"),
159
+ params or {},
160
+ )
161
+
162
+ def test_choose_view_shows_status_column(self):
163
+ response = self.get("choose")
164
+ html = response.json()["html"]
165
+ self.assertTagInHTML("<th>Status</th>", html)
166
+ self.assertTagInHTML('<span class="w-status">draft</span>', html)
167
+ self.assertTagInHTML(
168
+ '<span class="w-status w-status--primary">live</span>', html
169
+ )
170
+ self.assertTagInHTML(
171
+ '<span class="w-status w-status--primary">live + draft</span>', html
172
+ )
173
+
174
+ def test_choose_results_view_shows_status_column(self):
175
+ response = self.get("choose_results")
176
+ self.assertContains(response, "<th>Status</th>", html=True)
177
+ self.assertContains(response, '<span class="w-status">draft</span>', html=True)
178
+ self.assertContains(
179
+ response, '<span class="w-status w-status--primary">live</span>', html=True
180
+ )
181
+ self.assertContains(
182
+ response,
183
+ '<span class="w-status w-status--primary">live + draft</span>',
184
+ html=True,
185
+ )
186
+
187
+
188
+ class TestSnippetChooseWithSearchableSnippet(WagtailTestUtils, TransactionTestCase):
189
+ def setUp(self):
190
+ self.login()
191
+
192
+ # Create some instances of the searchable snippet for testing
193
+ self.snippet_a = SearchableSnippet.objects.create(text="Hello")
194
+ self.snippet_b = SearchableSnippet.objects.create(text="World")
195
+ self.snippet_c = SearchableSnippet.objects.create(text="Hello World")
196
+
197
+ def get(self, params=None):
198
+ return self.client.get(
199
+ reverse("wagtailsnippetchoosers_snippetstests_searchablesnippet:choose"),
200
+ params or {},
201
+ )
202
+
203
+ def test_simple(self):
204
+ response = self.get()
205
+ self.assertTemplateUsed(response, "wagtailadmin/generic/chooser/chooser.html")
206
+
207
+ # All snippets should be in items
208
+ items = list(response.context["results"].object_list)
209
+ self.assertIn(self.snippet_a, items)
210
+ self.assertIn(self.snippet_b, items)
211
+ self.assertIn(self.snippet_c, items)
212
+
213
+ def test_is_searchable(self):
214
+ # filter_form should have a search field
215
+ self.assertTrue(self.get().context["filter_form"].fields.get("q"))
216
+
217
+ def test_search_hello(self):
218
+ response = self.get({"q": "Hello"})
219
+
220
+ # Just snippets with "Hello" should be in items
221
+ items = list(response.context["results"].object_list)
222
+ self.assertIn(self.snippet_a, items)
223
+ self.assertNotIn(self.snippet_b, items)
224
+ self.assertIn(self.snippet_c, items)
225
+
226
+ def test_search_world(self):
227
+ response = self.get({"q": "World"})
228
+
229
+ # Just snippets with "World" should be in items
230
+ items = list(response.context["results"].object_list)
231
+ self.assertNotIn(self.snippet_a, items)
232
+ self.assertIn(self.snippet_b, items)
233
+ self.assertIn(self.snippet_c, items)
234
+
235
+ def test_partial_match(self):
236
+ response = self.get({"q": "hello wo"})
237
+
238
+ # should perform partial matching and return "Hello World"
239
+ items = list(response.context["results"].object_list)
240
+ self.assertNotIn(self.snippet_a, items)
241
+ self.assertNotIn(self.snippet_b, items)
242
+ self.assertIn(self.snippet_c, items)
243
+
244
+
245
+ class TestSnippetChooseWithNonAutocompleteSearchableSnippet(
246
+ WagtailTestUtils, TransactionTestCase
247
+ ):
248
+ """
249
+ Test that searchable snippets with no AutocompleteFields defined can still be searched using
250
+ full words
251
+ """
252
+
253
+ def setUp(self):
254
+ self.login()
255
+
256
+ # Create some instances of the searchable snippet for testing
257
+ self.snippet_a = NonAutocompleteSearchableSnippet.objects.create(text="Hello")
258
+ self.snippet_b = NonAutocompleteSearchableSnippet.objects.create(text="World")
259
+ self.snippet_c = NonAutocompleteSearchableSnippet.objects.create(
260
+ text="Hello World"
261
+ )
262
+
263
+ def get(self, params=None):
264
+ return self.client.get(
265
+ reverse(
266
+ "wagtailsnippetchoosers_snippetstests_nonautocompletesearchablesnippet:choose"
267
+ ),
268
+ params or {},
269
+ )
270
+
271
+ def test_search_hello(self):
272
+ with self.assertWarnsRegex(
273
+ RuntimeWarning, "does not specify any AutocompleteFields"
274
+ ):
275
+ response = self.get({"q": "Hello"})
276
+
277
+ # Just snippets with "Hello" should be in items
278
+ items = list(response.context["results"].object_list)
279
+ self.assertIn(self.snippet_a, items)
280
+ self.assertNotIn(self.snippet_b, items)
281
+ self.assertIn(self.snippet_c, items)
282
+
283
+
284
+ class TestSnippetChosen(WagtailTestUtils, TestCase):
285
+ fixtures = ["test.json"]
286
+
287
+ def setUp(self):
288
+ self.login()
289
+
290
+ def get(self, pk, params=None):
291
+ return self.client.get(
292
+ reverse("wagtailsnippetchoosers_tests_advert:chosen", args=(pk,)),
293
+ params or {},
294
+ )
295
+
296
+ def test_choose_a_page(self):
297
+ response = self.get(pk=Advert.objects.all()[0].pk)
298
+ response_json = json.loads(response.content.decode())
299
+ self.assertEqual(response_json["step"], "chosen")
300
+
301
+ def test_choose_a_non_existing_page(self):
302
+ response = self.get(999999)
303
+ self.assertEqual(response.status_code, 404)
304
+
305
+
306
+ class TestSnippetChooseWithCustomPrimaryKey(WagtailTestUtils, TestCase):
307
+ fixtures = ["test.json"]
308
+
309
+ def setUp(self):
310
+ self.login()
311
+
312
+ def get(self, params=None):
313
+ return self.client.get(
314
+ reverse("wagtailsnippetchoosers_tests_advertwithcustomprimarykey:choose"),
315
+ params or {},
316
+ )
317
+
318
+ def test_simple(self):
319
+ response = self.get()
320
+ self.assertTemplateUsed(response, "wagtailadmin/generic/chooser/chooser.html")
321
+ self.assertEqual(response.context["header_icon"], "snippet")
322
+ self.assertEqual(response.context["icon"], "snippet")
323
+
324
+ def test_ordering(self):
325
+ """
326
+ Listing should be ordered by PK if no ordering has been set on the model
327
+ """
328
+ AdvertWithCustomPrimaryKey.objects.all().delete()
329
+ for i in range(10, 0, -1):
330
+ AdvertWithCustomPrimaryKey.objects.create(pk=i, text="advert %d" % i)
331
+ response = self.get()
332
+ self.assertEqual(response.status_code, 200)
333
+ self.assertEqual(response.context["results"][0].text, "advert 1")
334
+
335
+
336
+ class TestSnippetChosenWithCustomPrimaryKey(WagtailTestUtils, TestCase):
337
+ fixtures = ["test.json"]
338
+
339
+ def setUp(self):
340
+ self.login()
341
+
342
+ def get(self, pk, params=None):
343
+ return self.client.get(
344
+ reverse(
345
+ "wagtailsnippetchoosers_tests_advertwithcustomprimarykey:chosen",
346
+ args=(quote(pk),),
347
+ ),
348
+ params or {},
349
+ )
350
+
351
+ def test_choose_a_page(self):
352
+ response = self.get(pk=AdvertWithCustomPrimaryKey.objects.all()[0].pk)
353
+ response_json = json.loads(response.content.decode())
354
+ self.assertEqual(response_json["step"], "chosen")
355
+
356
+
357
+ class TestSnippetChosenWithCustomUUIDPrimaryKey(WagtailTestUtils, TestCase):
358
+ fixtures = ["test.json"]
359
+
360
+ def setUp(self):
361
+ self.login()
362
+
363
+ def get(self, pk, params=None):
364
+ return self.client.get(
365
+ reverse(
366
+ "wagtailsnippetchoosers_tests_advertwithcustomuuidprimarykey:chosen",
367
+ args=(quote(pk),),
368
+ ),
369
+ params or {},
370
+ )
371
+
372
+ def test_choose_a_page(self):
373
+ response = self.get(pk=AdvertWithCustomUUIDPrimaryKey.objects.all()[0].pk)
374
+ response_json = json.loads(response.content.decode())
375
+ self.assertEqual(response_json["step"], "chosen")
@@ -0,0 +1,22 @@
1
+ from django.test import TestCase
2
+
3
+ from wagtail.snippets.widgets import (
4
+ AdminSnippetChooser,
5
+ SnippetChooserAdapter,
6
+ )
7
+ from wagtail.test.testapp.models import Advert
8
+ from wagtail.test.utils import WagtailTestUtils
9
+
10
+
11
+ class TestAdminSnippetChooserWidget(WagtailTestUtils, TestCase):
12
+ def test_adapt(self):
13
+ widget = AdminSnippetChooser(Advert)
14
+
15
+ js_args = SnippetChooserAdapter().js_args(widget)
16
+
17
+ self.assertEqual(len(js_args), 3)
18
+ self.assertInHTML(
19
+ '<input type="hidden" name="__NAME__" id="__ID__">', js_args[0]
20
+ )
21
+ self.assertIn("Choose advert", js_args[0])
22
+ self.assertEqual(js_args[1], "__ID__")
@@ -0,0 +1,167 @@
1
+ import datetime
2
+
3
+ from django.contrib.admin.utils import quote
4
+ from django.contrib.auth.models import Permission
5
+ from django.test import TestCase
6
+ from django.urls import reverse
7
+ from django.utils.timezone import make_aware
8
+
9
+ from wagtail.test.testapp.models import (
10
+ RevisableChildModel,
11
+ RevisableModel,
12
+ )
13
+ from wagtail.test.utils import WagtailTestUtils
14
+ from wagtail.test.utils.template_tests import AdminTemplateTestUtils
15
+
16
+
17
+ class TestCompareRevisions(AdminTemplateTestUtils, WagtailTestUtils, TestCase):
18
+ # Actual tests for the comparison classes can be found in test_compare.py
19
+
20
+ def setUp(self):
21
+ self.snippet = RevisableModel.objects.create(text="Initial revision")
22
+ self.initial_revision = self.snippet.save_revision()
23
+ self.initial_revision.created_at = make_aware(datetime.datetime(2022, 5, 10))
24
+ self.initial_revision.save()
25
+
26
+ self.snippet.text = "First edit"
27
+ self.snippet.save()
28
+ self.edit_revision = self.snippet.save_revision()
29
+ self.edit_revision.created_at = make_aware(datetime.datetime(2022, 5, 11))
30
+ self.edit_revision.save()
31
+
32
+ self.snippet.text = "Final revision"
33
+ self.snippet.save()
34
+ self.final_revision = self.snippet.save_revision()
35
+ self.final_revision.created_at = make_aware(datetime.datetime(2022, 5, 12))
36
+ self.final_revision.save()
37
+
38
+ self.login()
39
+
40
+ def get(self, revision_a_id, revision_b_id):
41
+ compare_url = reverse(
42
+ "wagtailsnippets_tests_revisablemodel:revisions_compare",
43
+ args=(quote(self.snippet.pk), revision_a_id, revision_b_id),
44
+ )
45
+ return self.client.get(compare_url)
46
+
47
+ def test_compare_revisions(self):
48
+ response = self.get(self.initial_revision.pk, self.edit_revision.pk)
49
+ self.assertEqual(response.status_code, 200)
50
+
51
+ self.assertContains(
52
+ response,
53
+ '<span class="deletion">Initial revision</span><span class="addition">First edit</span>',
54
+ html=True,
55
+ )
56
+
57
+ index_url = reverse("wagtailsnippets_tests_revisablemodel:list", args=[])
58
+ edit_url = reverse(
59
+ "wagtailsnippets_tests_revisablemodel:edit",
60
+ args=(self.snippet.id,),
61
+ )
62
+ history_url = reverse(
63
+ "wagtailsnippets_tests_revisablemodel:history",
64
+ args=(self.snippet.id,),
65
+ )
66
+
67
+ self.assertBreadcrumbsItemsRendered(
68
+ [
69
+ # "Snippets" index link is omitted as RevisableModel has its own menu item
70
+ {"url": index_url, "label": "Revisable models"},
71
+ {"url": edit_url, "label": str(self.snippet)},
72
+ {"url": history_url, "label": "History"},
73
+ {"url": "", "label": "Compare", "sublabel": str(self.snippet)},
74
+ ],
75
+ response.content,
76
+ )
77
+
78
+ soup = self.get_soup(response.content)
79
+ edit_button = soup.select_one(f"a.w-header-button[href='{edit_url}']")
80
+ self.assertIsNotNone(edit_button)
81
+ self.assertEqual(edit_button.text.strip(), "Edit")
82
+
83
+ def test_compare_revisions_earliest(self):
84
+ response = self.get("earliest", self.edit_revision.pk)
85
+ self.assertEqual(response.status_code, 200)
86
+
87
+ self.assertContains(
88
+ response,
89
+ '<span class="deletion">Initial revision</span><span class="addition">First edit</span>',
90
+ html=True,
91
+ )
92
+
93
+ def test_compare_revisions_latest(self):
94
+ response = self.get(self.edit_revision.id, "latest")
95
+ self.assertEqual(response.status_code, 200)
96
+
97
+ self.assertContains(
98
+ response,
99
+ '<span class="deletion">First edit</span><span class="addition">Final revision</span>',
100
+ html=True,
101
+ )
102
+
103
+ def test_compare_revisions_live(self):
104
+ # Mess with the live version, bypassing revisions
105
+ self.snippet.text = "Live edited"
106
+ self.snippet.save(update_fields=["text"])
107
+
108
+ response = self.get(self.final_revision.id, "live")
109
+ self.assertEqual(response.status_code, 200)
110
+
111
+ self.assertContains(
112
+ response,
113
+ '<span class="deletion">Final revision</span><span class="addition">Live edited</span>',
114
+ html=True,
115
+ )
116
+
117
+
118
+ class TestCompareRevisionsWithPerUserPanels(WagtailTestUtils, TestCase):
119
+ def setUp(self):
120
+ self.snippet = RevisableChildModel.objects.create(
121
+ text="Foo bar", secret_text="Secret text"
122
+ )
123
+ self.old_revision = self.snippet.save_revision()
124
+ self.snippet.text = "Foo baz"
125
+ self.snippet.secret_text = "Secret unseen note"
126
+ self.new_revision = self.snippet.save_revision()
127
+ self.compare_url = reverse(
128
+ "wagtailsnippets_tests_revisablechildmodel:revisions_compare",
129
+ args=(quote(self.snippet.pk), self.old_revision.pk, self.new_revision.pk),
130
+ )
131
+
132
+ def test_comparison_as_superuser(self):
133
+ self.login()
134
+ response = self.client.get(self.compare_url)
135
+ self.assertEqual(response.status_code, 200)
136
+
137
+ self.assertContains(
138
+ response,
139
+ 'Foo <span class="deletion">bar</span><span class="addition">baz</span>',
140
+ html=True,
141
+ )
142
+ self.assertContains(
143
+ response,
144
+ 'Secret <span class="deletion">text</span><span class="addition">unseen note</span>',
145
+ html=True,
146
+ )
147
+
148
+ def test_comparison_as_ordinary_user(self):
149
+ user = self.create_user(username="editor", password="password")
150
+ add_permission = Permission.objects.get(
151
+ content_type__app_label="tests", codename="change_revisablechildmodel"
152
+ )
153
+ admin_permission = Permission.objects.get(
154
+ content_type__app_label="wagtailadmin", codename="access_admin"
155
+ )
156
+ user.user_permissions.add(add_permission, admin_permission)
157
+ self.login(username="editor", password="password")
158
+
159
+ response = self.client.get(self.compare_url)
160
+ self.assertEqual(response.status_code, 200)
161
+
162
+ self.assertContains(
163
+ response,
164
+ 'Foo <span class="deletion">bar</span><span class="addition">baz</span>',
165
+ html=True,
166
+ )
167
+ self.assertNotContains(response, "unseen note")
@@ -0,0 +1,38 @@
1
+ from django.contrib.auth.models import Permission
2
+ from django.test import TestCase
3
+ from django.urls import reverse
4
+
5
+ from wagtail.test.snippets.models import StandardSnippet
6
+ from wagtail.test.utils import WagtailTestUtils
7
+
8
+
9
+ class TestSnippetCopyView(WagtailTestUtils, TestCase):
10
+ def setUp(self):
11
+ self.snippet = StandardSnippet.objects.create(text="Test snippet")
12
+ self.url = reverse(
13
+ StandardSnippet.snippet_viewset.get_url_name("copy"),
14
+ args=(self.snippet.pk,),
15
+ )
16
+ self.user = self.login()
17
+
18
+ def test_without_permission(self):
19
+ self.user.is_superuser = False
20
+ self.user.save()
21
+ admin_permission = Permission.objects.get(
22
+ content_type__app_label="wagtailadmin", codename="access_admin"
23
+ )
24
+ self.user.user_permissions.add(admin_permission)
25
+
26
+ response = self.client.get(self.url)
27
+ self.assertEqual(response.status_code, 302)
28
+ self.assertRedirects(response, reverse("wagtailadmin_home"))
29
+
30
+ def test_form_is_prefilled(self):
31
+ response = self.client.get(self.url)
32
+ self.assertEqual(response.status_code, 200)
33
+ self.assertTemplateUsed(response, "wagtailsnippets/snippets/create.html")
34
+
35
+ # Ensure form is prefilled
36
+ soup = self.get_soup(response.content)
37
+ text_input = soup.select_one('input[name="text"]')
38
+ self.assertEqual(text_input.attrs.get("value"), "Test snippet")