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,228 @@
1
+ from unittest import mock
2
+
3
+ from django.contrib.admin.utils import quote
4
+ from django.contrib.auth.models import Permission
5
+ from django.http import HttpRequest, HttpResponse
6
+ from django.test import TestCase
7
+ from django.urls import reverse
8
+
9
+ from wagtail.signals import unpublished
10
+ from wagtail.test.testapp.models import DraftStateCustomPrimaryKeyModel
11
+ from wagtail.test.utils.wagtail_tests import WagtailTestUtils
12
+
13
+
14
+ class TestSnippetUnpublish(WagtailTestUtils, TestCase):
15
+ def setUp(self):
16
+ self.user = self.login()
17
+ self.snippet = DraftStateCustomPrimaryKeyModel.objects.create(
18
+ custom_id="custom/1", text="to be unpublished"
19
+ )
20
+ self.unpublish_url = reverse(
21
+ "wagtailsnippets_tests_draftstatecustomprimarykeymodel:unpublish",
22
+ args=(quote(self.snippet.pk),),
23
+ )
24
+
25
+ def test_unpublish_view(self):
26
+ """
27
+ This tests that the unpublish view responds with an unpublish confirm page
28
+ """
29
+ # Get unpublish page
30
+ response = self.client.get(self.unpublish_url)
31
+
32
+ # Check that the user received an unpublish confirm page
33
+ self.assertEqual(response.status_code, 200)
34
+ self.assertTemplateUsed(response, "wagtailadmin/generic/confirm_unpublish.html")
35
+
36
+ def test_unpublish_view_invalid_pk(self):
37
+ """
38
+ This tests that the unpublish view returns an error if the object pk is invalid
39
+ """
40
+ # Get unpublish page
41
+ response = self.client.get(
42
+ reverse(
43
+ "wagtailsnippets_tests_draftstatecustomprimarykeymodel:unpublish",
44
+ args=(quote(12345),),
45
+ )
46
+ )
47
+
48
+ # Check that the user received a 404 response
49
+ self.assertEqual(response.status_code, 404)
50
+
51
+ def test_unpublish_view_get_bad_permissions(self):
52
+ """
53
+ This tests that the unpublish view doesn't allow users without unpublish permissions
54
+ """
55
+ # Remove privileges from user
56
+ self.user.is_superuser = False
57
+ self.user.user_permissions.add(
58
+ Permission.objects.get(
59
+ content_type__app_label="wagtailadmin", codename="access_admin"
60
+ )
61
+ )
62
+ self.user.save()
63
+
64
+ # Get unpublish page
65
+ response = self.client.get(self.unpublish_url)
66
+
67
+ # Check that the user received a 302 redirected response
68
+ self.assertEqual(response.status_code, 302)
69
+
70
+ def test_unpublish_view_post_bad_permissions(self):
71
+ """
72
+ This tests that the unpublish view doesn't allow users without unpublish permissions
73
+ """
74
+ # Connect a mock signal handler to unpublished signal
75
+ mock_handler = mock.MagicMock()
76
+ unpublished.connect(mock_handler)
77
+
78
+ try:
79
+ # Remove privileges from user
80
+ self.user.is_superuser = False
81
+ self.user.user_permissions.add(
82
+ Permission.objects.get(
83
+ content_type__app_label="wagtailadmin", codename="access_admin"
84
+ )
85
+ )
86
+ self.user.save()
87
+
88
+ # Post to the unpublish view
89
+ response = self.client.post(self.unpublish_url)
90
+
91
+ # Should be redirected to the home page
92
+ self.assertRedirects(response, reverse("wagtailadmin_home"))
93
+
94
+ # Check that the object was not unpublished
95
+ self.assertTrue(
96
+ DraftStateCustomPrimaryKeyModel.objects.get(pk=self.snippet.pk).live
97
+ )
98
+
99
+ # Check that the unpublished signal was not fired
100
+ self.assertEqual(mock_handler.call_count, 0)
101
+ finally:
102
+ unpublished.disconnect(mock_handler)
103
+
104
+ def test_unpublish_view_post_with_publish_permission(self):
105
+ """
106
+ This posts to the unpublish view and checks that the object was unpublished,
107
+ using a specific publish permission instead of relying on the superuser flag
108
+ """
109
+ # Connect a mock signal handler to unpublished signal
110
+ mock_handler = mock.MagicMock()
111
+ unpublished.connect(mock_handler)
112
+
113
+ try:
114
+ # Only add edit and publish permissions
115
+ self.user.is_superuser = False
116
+ edit_permission = Permission.objects.get(
117
+ content_type__app_label="tests",
118
+ codename="change_draftstatecustomprimarykeymodel",
119
+ )
120
+ publish_permission = Permission.objects.get(
121
+ content_type__app_label="tests",
122
+ codename="publish_draftstatecustomprimarykeymodel",
123
+ )
124
+ admin_permission = Permission.objects.get(
125
+ content_type__app_label="wagtailadmin", codename="access_admin"
126
+ )
127
+ self.user.user_permissions.add(
128
+ edit_permission,
129
+ publish_permission,
130
+ admin_permission,
131
+ )
132
+ self.user.save()
133
+
134
+ # Post to the unpublish view
135
+ response = self.client.post(self.unpublish_url)
136
+
137
+ # Should be redirected to the listing page
138
+ self.assertRedirects(
139
+ response,
140
+ reverse("wagtailsnippets_tests_draftstatecustomprimarykeymodel:list"),
141
+ )
142
+
143
+ # Check that the object was unpublished
144
+ self.assertFalse(
145
+ DraftStateCustomPrimaryKeyModel.objects.get(pk=self.snippet.pk).live
146
+ )
147
+
148
+ # Check that the unpublished signal was fired
149
+ self.assertEqual(mock_handler.call_count, 1)
150
+ mock_call = mock_handler.mock_calls[0][2]
151
+
152
+ self.assertEqual(mock_call["sender"], DraftStateCustomPrimaryKeyModel)
153
+ self.assertEqual(mock_call["instance"], self.snippet)
154
+ self.assertIsInstance(
155
+ mock_call["instance"], DraftStateCustomPrimaryKeyModel
156
+ )
157
+ finally:
158
+ unpublished.disconnect(mock_handler)
159
+
160
+ def test_unpublish_view_post(self):
161
+ """
162
+ This posts to the unpublish view and checks that the object was unpublished
163
+ """
164
+ # Connect a mock signal handler to unpublished signal
165
+ mock_handler = mock.MagicMock()
166
+ unpublished.connect(mock_handler)
167
+
168
+ try:
169
+ # Post to the unpublish view
170
+ response = self.client.post(self.unpublish_url)
171
+
172
+ # Should be redirected to the listing page
173
+ self.assertRedirects(
174
+ response,
175
+ reverse("wagtailsnippets_tests_draftstatecustomprimarykeymodel:list"),
176
+ )
177
+
178
+ # Check that the object was unpublished
179
+ self.assertFalse(
180
+ DraftStateCustomPrimaryKeyModel.objects.get(pk=self.snippet.pk).live
181
+ )
182
+
183
+ # Check that the unpublished signal was fired
184
+ self.assertEqual(mock_handler.call_count, 1)
185
+ mock_call = mock_handler.mock_calls[0][2]
186
+
187
+ self.assertEqual(mock_call["sender"], DraftStateCustomPrimaryKeyModel)
188
+ self.assertEqual(mock_call["instance"], self.snippet)
189
+ self.assertIsInstance(
190
+ mock_call["instance"], DraftStateCustomPrimaryKeyModel
191
+ )
192
+ finally:
193
+ unpublished.disconnect(mock_handler)
194
+
195
+ def test_after_unpublish_hook(self):
196
+ def hook_func(request, snippet):
197
+ self.assertIsInstance(request, HttpRequest)
198
+ self.assertEqual(snippet.pk, self.snippet.pk)
199
+
200
+ return HttpResponse("Overridden!")
201
+
202
+ with self.register_hook("after_unpublish", hook_func):
203
+ post_data = {}
204
+ response = self.client.post(self.unpublish_url, post_data)
205
+
206
+ self.assertEqual(response.status_code, 200)
207
+ self.assertEqual(response.content, b"Overridden!")
208
+
209
+ self.snippet.refresh_from_db()
210
+ self.assertEqual(self.snippet.status_string, "draft")
211
+
212
+ def test_before_unpublish(self):
213
+ def hook_func(request, snippet):
214
+ self.assertIsInstance(request, HttpRequest)
215
+ self.assertEqual(snippet.pk, self.snippet.pk)
216
+
217
+ return HttpResponse("Overridden!")
218
+
219
+ with self.register_hook("before_unpublish", hook_func):
220
+ post_data = {}
221
+ response = self.client.post(self.unpublish_url, post_data)
222
+
223
+ self.assertEqual(response.status_code, 200)
224
+ self.assertEqual(response.content, b"Overridden!")
225
+
226
+ # The hook response is served before unpublish is called.
227
+ self.snippet.refresh_from_db()
228
+ self.assertEqual(self.snippet.status_string, "live")
@@ -0,0 +1,151 @@
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 now
8
+
9
+ from wagtail.models import Revision
10
+ from wagtail.test.testapp.models import DraftStateCustomPrimaryKeyModel
11
+ from wagtail.test.utils import WagtailTestUtils
12
+
13
+
14
+ class TestSnippetUnschedule(WagtailTestUtils, TestCase):
15
+ def setUp(self):
16
+ self.user = self.login()
17
+ self.test_snippet = DraftStateCustomPrimaryKeyModel.objects.create(
18
+ custom_id="custom/1", text="Draft-enabled Foo", live=False
19
+ )
20
+ self.go_live_at = now() + datetime.timedelta(days=1)
21
+ self.test_snippet.text = "I've been edited!"
22
+ self.test_snippet.go_live_at = self.go_live_at
23
+ self.latest_revision = self.test_snippet.save_revision()
24
+ self.latest_revision.publish()
25
+ self.test_snippet.refresh_from_db()
26
+ self.unschedule_url = reverse(
27
+ "wagtailsnippets_tests_draftstatecustomprimarykeymodel:revisions_unschedule",
28
+ args=[quote(self.test_snippet.pk), self.latest_revision.pk],
29
+ )
30
+
31
+ def set_permissions(self, set_publish_permission):
32
+ self.user.is_superuser = False
33
+
34
+ permissions = [
35
+ Permission.objects.get(
36
+ content_type__app_label="tests",
37
+ codename="change_draftstatecustomprimarykeymodel",
38
+ ),
39
+ Permission.objects.get(
40
+ content_type__app_label="wagtailadmin", codename="access_admin"
41
+ ),
42
+ ]
43
+
44
+ if set_publish_permission:
45
+ permissions.append(
46
+ Permission.objects.get(
47
+ content_type__app_label="tests",
48
+ codename="publish_draftstatecustomprimarykeymodel",
49
+ )
50
+ )
51
+
52
+ self.user.user_permissions.add(*permissions)
53
+ self.user.save()
54
+
55
+ def test_get_unschedule_view_with_publish_permissions(self):
56
+ self.set_permissions(True)
57
+
58
+ # Get unschedule page
59
+ response = self.client.get(self.unschedule_url)
60
+
61
+ # Check that the user received a confirmation page
62
+ self.assertEqual(response.status_code, 200)
63
+ self.assertTemplateUsed(
64
+ response, "wagtailadmin/shared/revisions/confirm_unschedule.html"
65
+ )
66
+
67
+ def test_get_unschedule_view_bad_permissions(self):
68
+ self.set_permissions(False)
69
+
70
+ # Get unschedule page
71
+ response = self.client.get(self.unschedule_url)
72
+
73
+ # Check that the user is redirected to the admin homepage
74
+ self.assertRedirects(response, reverse("wagtailadmin_home"))
75
+
76
+ def test_post_unschedule_view_with_publish_permissions(self):
77
+ self.set_permissions(True)
78
+
79
+ # Post unschedule page
80
+ response = self.client.post(self.unschedule_url)
81
+
82
+ # Check that the user was redirected to the history page
83
+ self.assertRedirects(
84
+ response,
85
+ reverse(
86
+ "wagtailsnippets_tests_draftstatecustomprimarykeymodel:history",
87
+ args=[quote(self.test_snippet.pk)],
88
+ ),
89
+ )
90
+
91
+ self.test_snippet.refresh_from_db()
92
+ self.latest_revision.refresh_from_db()
93
+
94
+ # Check that the revision is no longer scheduled
95
+ self.assertIsNone(self.latest_revision.approved_go_live_at)
96
+
97
+ # No revisions with approved_go_live_at
98
+ self.assertFalse(
99
+ Revision.objects.for_instance(self.test_snippet)
100
+ .exclude(approved_go_live_at__isnull=True)
101
+ .exists()
102
+ )
103
+
104
+ def test_post_unschedule_view_bad_permissions(self):
105
+ self.set_permissions(False)
106
+
107
+ # Post unschedule page
108
+ response = self.client.post(self.unschedule_url)
109
+
110
+ # Check that the user is redirected to the admin homepage
111
+ self.assertRedirects(response, reverse("wagtailadmin_home"))
112
+
113
+ self.test_snippet.refresh_from_db()
114
+ self.latest_revision.refresh_from_db()
115
+
116
+ # Check that the revision is still scheduled
117
+ self.assertIsNotNone(self.latest_revision.approved_go_live_at)
118
+
119
+ # Revision with approved_go_live_at exists
120
+ self.assertTrue(
121
+ Revision.objects.for_instance(self.test_snippet)
122
+ .exclude(approved_go_live_at__isnull=True)
123
+ .exists()
124
+ )
125
+
126
+ def test_post_unschedule_view_with_next_url(self):
127
+ self.set_permissions(True)
128
+
129
+ edit_url = reverse(
130
+ "wagtailsnippets_tests_draftstatecustomprimarykeymodel:edit",
131
+ args=[quote(self.test_snippet.pk)],
132
+ )
133
+
134
+ # Post unschedule page
135
+ response = self.client.post(self.unschedule_url + f"?next={edit_url}")
136
+
137
+ # Check that the user was redirected to the next url
138
+ self.assertRedirects(response, edit_url)
139
+
140
+ self.test_snippet.refresh_from_db()
141
+ self.latest_revision.refresh_from_db()
142
+
143
+ # Check that the revision is no longer scheduled
144
+ self.assertIsNone(self.latest_revision.approved_go_live_at)
145
+
146
+ # No revisions with approved_go_live_at
147
+ self.assertFalse(
148
+ Revision.objects.for_instance(self.test_snippet)
149
+ .exclude(approved_go_live_at__isnull=True)
150
+ .exists()
151
+ )
@@ -33,6 +33,8 @@ from wagtail.snippets.views.snippets import SnippetViewSet
33
33
  from wagtail.snippets.widgets import AdminSnippetChooser
34
34
  from wagtail.test.testapp.models import (
35
35
  Advert,
36
+ AdvertTag,
37
+ AdvertWithCustomUUIDPrimaryKey,
36
38
  DraftStateModel,
37
39
  FullFeaturedSnippet,
38
40
  ModeratedModel,
@@ -60,6 +62,21 @@ class TestIncorrectRegistration(SimpleTestCase):
60
62
  )
61
63
 
62
64
 
65
+ class TestIncorrectConverter(SimpleTestCase):
66
+ def test_unknown_converter(self):
67
+ class BadViewSet(SnippetViewSet):
68
+ model = AdvertTag
69
+ pk_path_converter = "foo"
70
+
71
+ with self.assertRaises(ImproperlyConfigured) as cm:
72
+ register_snippet(BadViewSet)
73
+ message = str(cm.exception)
74
+ self.assertEqual(
75
+ "BadViewSet.pk_path_converter is not a registered path converter",
76
+ message,
77
+ )
78
+
79
+
63
80
  class BaseSnippetViewSetTests(WagtailTestUtils, TestCase):
64
81
  model = None
65
82
 
@@ -383,6 +400,20 @@ class TestAdminURLs(BaseSnippetViewSetTests):
383
400
  expected_choose_url,
384
401
  )
385
402
 
403
+ def test_cannot_reverse_mismatched_converter_value(self):
404
+ viewset = AdvertWithCustomUUIDPrimaryKey.snippet_viewset
405
+ with self.assertRaises(NoReverseMatch):
406
+ reverse(viewset.get_url_name("edit"), kwargs={"pk": 123})
407
+
408
+ def test_404_on_mismatched_converter_value(self):
409
+ viewsets = [
410
+ AdvertWithCustomUUIDPrimaryKey.snippet_viewset,
411
+ FullFeaturedSnippet.snippet_viewset,
412
+ ]
413
+ for viewset in viewsets:
414
+ response = self.client.get(f"/admin/{viewset.url_prefix}/edit/123abc/")
415
+ self.assertEqual(response.status_code, 404)
416
+
386
417
 
387
418
  class TestPagination(BaseSnippetViewSetTests):
388
419
  @classmethod
@@ -444,7 +475,7 @@ class TestPagination(BaseSnippetViewSetTests):
444
475
  class TestFilterSetClass(BaseSnippetViewSetTests):
445
476
  model = FullFeaturedSnippet
446
477
 
447
- def get(self, params={}):
478
+ def get(self, params=None):
448
479
  return self.client.get(self.get_url("list"), params)
449
480
 
450
481
  def create_test_snippets(self):
@@ -570,7 +601,7 @@ class TestFilterSetClassSearch(WagtailTestUtils, TransactionTestCase):
570
601
  FullFeaturedSnippet.snippet_viewset.get_url_name(url_name), args=args
571
602
  )
572
603
 
573
- def get(self, params={}):
604
+ def get(self, params=None):
574
605
  return self.client.get(self.get_url("list"), params)
575
606
 
576
607
  def create_test_snippets(self):
@@ -611,7 +642,7 @@ class TestListFilterWithList(BaseSnippetViewSetTests):
611
642
  self.date = now()
612
643
  self.date_str = self.date.isoformat()
613
644
 
614
- def get(self, params={}):
645
+ def get(self, params=None):
615
646
  return self.client.get(self.get_url("list"), params)
616
647
 
617
648
  def create_test_snippets(self):
@@ -746,7 +777,7 @@ class TestListViewWithCustomColumns(BaseSnippetViewSetTests):
746
777
  cls.model.objects.create(text="From Indonesia", country_code="ID")
747
778
  cls.model.objects.create(text="From the UK", country_code="UK")
748
779
 
749
- def get(self, params={}):
780
+ def get(self, params=None):
750
781
  return self.client.get(self.get_url("list"), params)
751
782
 
752
783
  def test_custom_columns(self):
@@ -1091,7 +1122,7 @@ class TestDjangoORMSearchBackend(BaseSnippetViewSetTests):
1091
1122
  text="Python is a programming-bas, uh, language",
1092
1123
  )
1093
1124
 
1094
- def get(self, params={}, url_name="list"):
1125
+ def get(self, params=None, url_name="list"):
1095
1126
  return self.client.get(self.get_url(url_name), params)
1096
1127
 
1097
1128
  def test_simple(self):
@@ -1658,6 +1689,7 @@ class TestCustomMethods(BaseSnippetViewSetTests):
1658
1689
  {
1659
1690
  "data-controller": "w-teleport",
1660
1691
  "data-w-teleport-target-value": "#w-slim-header-buttons",
1692
+ "data-w-teleport-mode-value": "innerHTML",
1661
1693
  },
1662
1694
  )
1663
1695
  self.assertIsNotNone(template)
@@ -1675,6 +1707,7 @@ class TestCustomMethods(BaseSnippetViewSetTests):
1675
1707
  {
1676
1708
  "data-controller": "w-teleport",
1677
1709
  "data-w-teleport-target-value": "#w-slim-header-buttons",
1710
+ "data-w-teleport-mode-value": "innerHTML",
1678
1711
  },
1679
1712
  )
1680
1713
  self.assertIsNotNone(template)