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
@@ -345,6 +345,34 @@ class TestEditLockedSnippet(BaseLockingTestCase):
345
345
  # Check that the snippet is not edited
346
346
  self.assertEqual(self.snippet.text, "I'm a lockable snippet!")
347
347
 
348
+ def test_edit_post_locked_with_json_response(self):
349
+ # Lock the snippet
350
+ self.lock_snippet(self.create_user("user2"))
351
+
352
+ # Try to edit the snippet
353
+ response = self.client.post(
354
+ self.get_url("edit"),
355
+ {"text": "Edited while locked"},
356
+ headers={"Accept": "application/json"},
357
+ )
358
+ self.refresh_snippet()
359
+
360
+ self.assertEqual(response.status_code, 400)
361
+ self.assertEqual(
362
+ response.json(),
363
+ {
364
+ "success": False,
365
+ "error_code": "locked",
366
+ "error_message": f"The {self.model_name} could not be saved as it is locked",
367
+ },
368
+ )
369
+
370
+ # Check that the snippet is still locked
371
+ self.assertTrue(self.snippet.locked)
372
+
373
+ # Check that the snippet is not edited
374
+ self.assertEqual(self.snippet.text, "I'm a lockable snippet!")
375
+
348
376
  def test_edit_post_locked_by_self(self):
349
377
  """A user can edit a snippet that is locked by themselves."""
350
378
  # Lock the snippet
@@ -0,0 +1,167 @@
1
+ from django.contrib.admin.utils import quote
2
+ from django.contrib.auth.models import Permission
3
+ from django.test import TestCase
4
+ from django.urls import reverse
5
+
6
+ from wagtail.test.testapp.models import Advert
7
+ from wagtail.test.utils import WagtailTestUtils
8
+
9
+
10
+ class TestAddOnlyPermissions(WagtailTestUtils, TestCase):
11
+ fixtures = ["test.json"]
12
+
13
+ def setUp(self):
14
+ self.test_snippet = Advert.objects.get(pk=1)
15
+
16
+ # Create a user with add_advert permission but not change_advert
17
+ user = self.create_user(
18
+ username="addonly", email="addonly@example.com", password="password"
19
+ )
20
+ add_permission = Permission.objects.get(
21
+ content_type__app_label="tests", codename="add_advert"
22
+ )
23
+ admin_permission = Permission.objects.get(
24
+ content_type__app_label="wagtailadmin", codename="access_admin"
25
+ )
26
+ user.user_permissions.add(add_permission, admin_permission)
27
+ self.login(username="addonly", password="password")
28
+
29
+ def test_get_index(self):
30
+ response = self.client.get(reverse("wagtailsnippets_tests_advert:list"))
31
+ self.assertEqual(response.status_code, 200)
32
+ self.assertTemplateUsed(response, "wagtailsnippets/snippets/index.html")
33
+
34
+ # user should get an "Add advert" button
35
+ self.assertContains(response, "Add advert")
36
+
37
+ def test_get_add(self):
38
+ response = self.client.get(reverse("wagtailsnippets_tests_advert:add"))
39
+ self.assertEqual(response.status_code, 200)
40
+ self.assertTemplateUsed(response, "wagtailsnippets/snippets/create.html")
41
+ self.assertEqual(response.context["header_icon"], "snippet")
42
+
43
+ def test_get_edit(self):
44
+ response = self.client.get(
45
+ reverse(
46
+ "wagtailsnippets_tests_advert:edit",
47
+ args=[quote(self.test_snippet.pk)],
48
+ )
49
+ )
50
+ # permission should be denied
51
+ self.assertRedirects(response, reverse("wagtailadmin_home"))
52
+
53
+ def test_get_delete(self):
54
+ response = self.client.get(
55
+ reverse(
56
+ "wagtailsnippets_tests_advert:delete",
57
+ args=[quote(self.test_snippet.pk)],
58
+ )
59
+ )
60
+ # permission should be denied
61
+ self.assertRedirects(response, reverse("wagtailadmin_home"))
62
+
63
+
64
+ class TestEditOnlyPermissions(WagtailTestUtils, TestCase):
65
+ fixtures = ["test.json"]
66
+
67
+ def setUp(self):
68
+ self.test_snippet = Advert.objects.get(pk=1)
69
+
70
+ # Create a user with change_advert permission but not add_advert
71
+ user = self.create_user(
72
+ username="changeonly", email="changeonly@example.com", password="password"
73
+ )
74
+ change_permission = Permission.objects.get(
75
+ content_type__app_label="tests", codename="change_advert"
76
+ )
77
+ admin_permission = Permission.objects.get(
78
+ content_type__app_label="wagtailadmin", codename="access_admin"
79
+ )
80
+ user.user_permissions.add(change_permission, admin_permission)
81
+ self.login(username="changeonly", password="password")
82
+
83
+ def test_get_index(self):
84
+ response = self.client.get(reverse("wagtailsnippets_tests_advert:list"))
85
+ self.assertEqual(response.status_code, 200)
86
+ self.assertTemplateUsed(response, "wagtailsnippets/snippets/index.html")
87
+
88
+ # user should not get an "Add advert" button
89
+ self.assertNotContains(response, "Add advert")
90
+
91
+ def test_get_add(self):
92
+ response = self.client.get(reverse("wagtailsnippets_tests_advert:add"))
93
+ # permission should be denied
94
+ self.assertRedirects(response, reverse("wagtailadmin_home"))
95
+
96
+ def test_get_edit(self):
97
+ response = self.client.get(
98
+ reverse(
99
+ "wagtailsnippets_tests_advert:edit",
100
+ args=[quote(self.test_snippet.pk)],
101
+ )
102
+ )
103
+ self.assertEqual(response.status_code, 200)
104
+ self.assertTemplateUsed(response, "wagtailsnippets/snippets/edit.html")
105
+ self.assertEqual(response.context["header_icon"], "snippet")
106
+
107
+ def test_get_delete(self):
108
+ response = self.client.get(
109
+ reverse(
110
+ "wagtailsnippets_tests_advert:delete",
111
+ args=[quote(self.test_snippet.pk)],
112
+ )
113
+ )
114
+ # permission should be denied
115
+ self.assertRedirects(response, reverse("wagtailadmin_home"))
116
+
117
+
118
+ class TestDeleteOnlyPermissions(WagtailTestUtils, TestCase):
119
+ fixtures = ["test.json"]
120
+
121
+ def setUp(self):
122
+ self.test_snippet = Advert.objects.get(pk=1)
123
+
124
+ # Create a user with delete_advert permission
125
+ user = self.create_user(username="deleteonly", password="password")
126
+ change_permission = Permission.objects.get(
127
+ content_type__app_label="tests", codename="delete_advert"
128
+ )
129
+ admin_permission = Permission.objects.get(
130
+ content_type__app_label="wagtailadmin", codename="access_admin"
131
+ )
132
+ user.user_permissions.add(change_permission, admin_permission)
133
+ self.login(username="deleteonly", password="password")
134
+
135
+ def test_get_index(self):
136
+ response = self.client.get(reverse("wagtailsnippets_tests_advert:list"))
137
+ self.assertEqual(response.status_code, 200)
138
+ self.assertTemplateUsed(response, "wagtailsnippets/snippets/index.html")
139
+
140
+ # user should not get an "Add advert" button
141
+ self.assertNotContains(response, "Add advert")
142
+
143
+ def test_get_add(self):
144
+ response = self.client.get(reverse("wagtailsnippets_tests_advert:add"))
145
+ # permission should be denied
146
+ self.assertRedirects(response, reverse("wagtailadmin_home"))
147
+
148
+ def test_get_edit(self):
149
+ response = self.client.get(
150
+ reverse(
151
+ "wagtailsnippets_tests_advert:edit",
152
+ args=[quote(self.test_snippet.pk)],
153
+ )
154
+ )
155
+ # permission should be denied
156
+ self.assertRedirects(response, reverse("wagtailadmin_home"))
157
+
158
+ def test_get_delete(self):
159
+ response = self.client.get(
160
+ reverse(
161
+ "wagtailsnippets_tests_advert:delete",
162
+ args=[quote(self.test_snippet.pk)],
163
+ )
164
+ )
165
+ self.assertEqual(response.status_code, 200)
166
+ self.assertTemplateUsed(response, "wagtailadmin/generic/confirm_delete.html")
167
+ self.assertEqual(response.context["header_icon"], "snippet")
@@ -480,7 +480,7 @@ class TestEnablePreview(WagtailTestUtils, TestCase):
480
480
  def test_show_preview_panel_on_edit_with_single_mode(self):
481
481
  edit_url = self.get_url(self.single, "edit", args=(self.single.pk,))
482
482
  preview_url = self.get_url(
483
- self.single, "preview_on_edit", args=(self.multiple.pk,)
483
+ self.single, "preview_on_edit", args=(self.single.pk,)
484
484
  )
485
485
  new_tab_url = preview_url + "?mode="
486
486
  response = self.client.get(edit_url)
@@ -574,7 +574,7 @@ class TestEnablePreview(WagtailTestUtils, TestCase):
574
574
  def test_custom_auto_update_interval(self):
575
575
  edit_url = self.get_url(self.single, "edit", args=(self.single.pk,))
576
576
  preview_url = self.get_url(
577
- self.single, "preview_on_edit", args=(self.multiple.pk,)
577
+ self.single, "preview_on_edit", args=(self.single.pk,)
578
578
  )
579
579
  response = self.client.get(edit_url)
580
580
 
@@ -603,7 +603,7 @@ class TestEnablePreview(WagtailTestUtils, TestCase):
603
603
  def test_disable_auto_update_using_zero_interval(self):
604
604
  edit_url = self.get_url(self.single, "edit", args=(self.single.pk,))
605
605
  preview_url = self.get_url(
606
- self.single, "preview_on_edit", args=(self.multiple.pk,)
606
+ self.single, "preview_on_edit", args=(self.single.pk,)
607
607
  )
608
608
  response = self.client.get(edit_url)
609
609
 
@@ -0,0 +1,343 @@
1
+ from django.conf import settings
2
+ from django.contrib.admin.utils import quote
3
+ from django.contrib.auth.models import Permission
4
+ from django.contrib.contenttypes.models import ContentType
5
+ from django.test import TestCase
6
+ from django.urls import reverse
7
+ from django.utils.timezone import now
8
+ from freezegun import freeze_time
9
+
10
+ from wagtail.models import ModelLogEntry
11
+ from wagtail.test.testapp.models import (
12
+ Advert,
13
+ DraftStateModel,
14
+ MultiPreviewModesModel,
15
+ RevisableModel,
16
+ )
17
+ from wagtail.test.utils import WagtailTestUtils
18
+
19
+
20
+ class TestSnippetRevisions(WagtailTestUtils, TestCase):
21
+ @property
22
+ def revert_url(self):
23
+ return self.get_url(
24
+ "revisions_revert", args=[quote(self.snippet.pk), self.initial_revision.pk]
25
+ )
26
+
27
+ def get(self):
28
+ return self.client.get(self.revert_url)
29
+
30
+ def post(self, post_data=None):
31
+ return self.client.post(self.revert_url, post_data)
32
+
33
+ def get_url(self, url_name, args=None):
34
+ view_name = self.snippet.snippet_viewset.get_url_name(url_name)
35
+ if args is None:
36
+ args = [quote(self.snippet.pk)]
37
+ return reverse(view_name, args=args)
38
+
39
+ def setUp(self):
40
+ self.user = self.login()
41
+
42
+ with freeze_time("2022-05-10 11:00:00"):
43
+ self.snippet = RevisableModel.objects.create(text="The original text")
44
+ self.initial_revision = self.snippet.save_revision(user=self.user)
45
+ ModelLogEntry.objects.create(
46
+ content_type=ContentType.objects.get_for_model(RevisableModel),
47
+ label="The original text",
48
+ action="wagtail.create",
49
+ timestamp=now(),
50
+ object_id=self.snippet.pk,
51
+ revision=self.initial_revision,
52
+ content_changed=True,
53
+ )
54
+
55
+ self.snippet.text = "The edited text"
56
+ self.snippet.save()
57
+ self.edit_revision = self.snippet.save_revision(user=self.user, log_action=True)
58
+
59
+ def test_get_revert_revision(self):
60
+ response = self.get()
61
+ self.assertEqual(response.status_code, 200)
62
+
63
+ if settings.USE_TZ:
64
+ # the default timezone is "Asia/Tokyo", so we expect UTC +9
65
+ expected_date_string = "May 10, 2022, 8 p.m."
66
+ else:
67
+ expected_date_string = "May 10, 2022, 11 a.m."
68
+
69
+ # Message should be shown
70
+ self.assertContains(
71
+ response,
72
+ f"You are viewing a previous version of this Revisable model from <b>{expected_date_string}</b> by",
73
+ count=1,
74
+ )
75
+
76
+ # Form should show the content of the revision, not the current draft
77
+ soup = self.get_soup(response.content)
78
+ textarea = soup.select_one("textarea[name='text']")
79
+ self.assertIsNotNone(textarea)
80
+ self.assertEqual(textarea.text.strip(), "The original text")
81
+
82
+ # Form action url should point to the revisions_revert view
83
+ form_tag = f'<form action="{self.revert_url}" method="POST">'
84
+ html = response.content.decode()
85
+ self.assertTagInHTML(form_tag, html, count=1, allow_extra_attrs=True)
86
+
87
+ # Buttons should be relabelled
88
+ self.assertContains(response, "Replace current revision", count=1)
89
+
90
+ soup = self.get_soup(response.content)
91
+ form = soup.select_one("form[data-edit-form]")
92
+ self.assertIsNotNone(form)
93
+
94
+ # Autosave should be disabled
95
+ self.assertNotIn("w-autosave", form["data-controller"].split())
96
+ self.assertNotIn("w-autosave", form["data-action"])
97
+ self.assertIsNone(form.attrs.get("data-w-autosave-interval-value"))
98
+
99
+ def test_get_revert_revision_with_non_revisable_snippet(self):
100
+ snippet = Advert.objects.create(text="foo")
101
+ response = self.client.get(
102
+ f"/admin/snippets/tests/advert/history/{snippet.pk}/revisions/1/revert/"
103
+ )
104
+ self.assertEqual(response.status_code, 404)
105
+
106
+ def test_get_with_limited_permissions(self):
107
+ self.user.is_superuser = False
108
+ self.user.user_permissions.add(
109
+ Permission.objects.get(
110
+ content_type__app_label="wagtailadmin", codename="access_admin"
111
+ )
112
+ )
113
+ self.user.save()
114
+
115
+ response = self.get()
116
+ self.assertEqual(response.status_code, 302)
117
+
118
+ def test_get_with_draft_state_snippet(self):
119
+ self.snippet = DraftStateModel.objects.create(text="Draft-enabled Foo")
120
+ self.initial_revision = self.snippet.save_revision()
121
+ response = self.get()
122
+
123
+ self.assertEqual(response.status_code, 200)
124
+ self.assertTemplateUsed(response, "wagtailsnippets/snippets/edit.html")
125
+ soup = self.get_soup(response.content)
126
+
127
+ # The save button should be labelled "Replace current draft"
128
+ footer = soup.select_one("footer")
129
+ save_button = footer.select_one(
130
+ 'button[type="submit"]:not([name="action-publish"])'
131
+ )
132
+ self.assertIsNotNone(save_button)
133
+ self.assertEqual(save_button.text.strip(), "Replace current draft")
134
+ # The publish button should exist and have name="action-publish"
135
+ publish_button = footer.select_one(
136
+ 'button[type="submit"][name="action-publish"]'
137
+ )
138
+ self.assertIsNotNone(publish_button)
139
+ self.assertEqual(publish_button.text.strip(), "Publish this version")
140
+ self.assertEqual(
141
+ set(publish_button.get("class")),
142
+ {"button", "action-save", "button-longrunning"},
143
+ )
144
+
145
+ # Should not show the Unpublish action menu item
146
+ unpublish_url = reverse(
147
+ "wagtailsnippets_tests_draftstatemodel:unpublish",
148
+ args=(quote(self.snippet.pk),),
149
+ )
150
+ unpublish_button = footer.select_one(f'a[href="{unpublish_url}"]')
151
+ self.assertIsNone(unpublish_button)
152
+
153
+ def test_get_with_previewable_snippet(self):
154
+ self.snippet = MultiPreviewModesModel.objects.create(text="Preview-enabled foo")
155
+ self.initial_revision = self.snippet.save_revision()
156
+
157
+ self.snippet.text = "Preview-enabled bar"
158
+ self.snippet.save_revision()
159
+
160
+ response = self.get()
161
+
162
+ self.assertEqual(response.status_code, 200)
163
+ self.assertTemplateUsed(response, "wagtailsnippets/snippets/edit.html")
164
+
165
+ # Message should be shown
166
+ self.assertContains(
167
+ response,
168
+ "You are viewing a previous version of this",
169
+ count=1,
170
+ )
171
+
172
+ # Form should show the content of the revision, not the current draft
173
+ self.assertContains(response, "Preview-enabled foo")
174
+
175
+ # Form action url should point to the revisions_revert view
176
+ form_tag = f'<form action="{self.revert_url}" method="POST">'
177
+ html = response.content.decode()
178
+ self.assertTagInHTML(form_tag, html, count=1, allow_extra_attrs=True)
179
+
180
+ # Buttons should be relabelled
181
+ self.assertContains(response, "Replace current revision", count=1)
182
+
183
+ # Should show the preview panel
184
+ preview_url = self.get_url("preview_on_edit")
185
+ self.assertContains(response, 'data-side-panel="preview"')
186
+ soup = self.get_soup(response.content)
187
+ controller = soup.select_one('[data-controller="w-preview"]')
188
+ self.assertIsNotNone(controller)
189
+ self.assertEqual(controller.get("data-w-preview-url-value"), preview_url)
190
+
191
+ # Should have the preview side panel toggle button
192
+ toggle_button = soup.find("button", {"data-side-panel-toggle": "preview"})
193
+ self.assertIsNotNone(toggle_button)
194
+ self.assertEqual("w-tooltip w-kbd", toggle_button["data-controller"])
195
+ self.assertEqual("mod+p", toggle_button["data-w-kbd-key-value"])
196
+
197
+ def test_replace_revision(self):
198
+ get_response = self.get()
199
+ text_from_revision = get_response.context["form"].initial["text"]
200
+
201
+ post_response = self.post(
202
+ post_data={
203
+ "text": text_from_revision + " reverted",
204
+ "revision": self.initial_revision.pk,
205
+ }
206
+ )
207
+ self.assertRedirects(post_response, self.get_url("list", args=[]))
208
+
209
+ self.snippet.refresh_from_db()
210
+ latest_revision = self.snippet.get_latest_revision()
211
+ log_entry = ModelLogEntry.objects.filter(revision=latest_revision).first()
212
+
213
+ # The instance should be updated
214
+ self.assertEqual(self.snippet.text, "The original text reverted")
215
+ # The initial revision, edited revision, and revert revision
216
+ self.assertEqual(self.snippet.revisions.count(), 3)
217
+ # The latest revision should be the revert revision
218
+ self.assertEqual(latest_revision.content["text"], "The original text reverted")
219
+
220
+ # A new log entry with "wagtail.revert" action should be created
221
+ self.assertIsNotNone(log_entry)
222
+ self.assertEqual(log_entry.action, "wagtail.revert")
223
+
224
+ def test_replace_with_limited_permissions(self):
225
+ self.user.is_superuser = False
226
+ self.user.user_permissions.add(
227
+ Permission.objects.get(
228
+ content_type__app_label="wagtailadmin", codename="access_admin"
229
+ )
230
+ )
231
+ self.user.save()
232
+
233
+ response = self.post(
234
+ post_data={
235
+ "text": "test text",
236
+ "revision": self.initial_revision.pk,
237
+ }
238
+ )
239
+ self.assertEqual(response.status_code, 302)
240
+
241
+ self.snippet.refresh_from_db()
242
+ self.assertNotEqual(self.snippet.text, "test text")
243
+
244
+ # Only the initial revision and edited revision, no revert revision
245
+ self.assertEqual(self.snippet.revisions.count(), 2)
246
+
247
+ def test_replace_draft(self):
248
+ self.snippet = DraftStateModel.objects.create(
249
+ text="Draft-enabled Foo", live=False
250
+ )
251
+ self.initial_revision = self.snippet.save_revision()
252
+ self.snippet.text = "Draft-enabled Foo edited"
253
+ self.edit_revision = self.snippet.save_revision()
254
+ get_response = self.get()
255
+ text_from_revision = get_response.context["form"].initial["text"]
256
+
257
+ post_response = self.post(
258
+ post_data={
259
+ "text": text_from_revision + " reverted",
260
+ "revision": self.initial_revision.pk,
261
+ }
262
+ )
263
+ self.assertRedirects(post_response, self.get_url("edit"))
264
+
265
+ self.snippet.refresh_from_db()
266
+ latest_revision = self.snippet.get_latest_revision()
267
+ log_entry = ModelLogEntry.objects.filter(revision=latest_revision).first()
268
+ publish_log_entries = ModelLogEntry.objects.filter(
269
+ content_type=ContentType.objects.get_for_model(DraftStateModel),
270
+ action="wagtail.publish",
271
+ object_id=self.snippet.pk,
272
+ )
273
+
274
+ # The instance should be updated, since it is still a draft
275
+ self.assertEqual(self.snippet.text, "Draft-enabled Foo reverted")
276
+ # The initial revision, edited revision, and revert revision
277
+ self.assertEqual(self.snippet.revisions.count(), 3)
278
+ # The latest revision should be the revert revision
279
+ self.assertEqual(latest_revision.content["text"], "Draft-enabled Foo reverted")
280
+
281
+ # A new log entry with "wagtail.revert" action should be created
282
+ self.assertIsNotNone(log_entry)
283
+ self.assertEqual(log_entry.action, "wagtail.revert")
284
+
285
+ # There should be no log entries for the publish action
286
+ self.assertEqual(publish_log_entries.count(), 0)
287
+
288
+ # The instance should still be a draft
289
+ self.assertFalse(self.snippet.live)
290
+ self.assertTrue(self.snippet.has_unpublished_changes)
291
+ self.assertIsNone(self.snippet.first_published_at)
292
+ self.assertIsNone(self.snippet.last_published_at)
293
+ self.assertIsNone(self.snippet.live_revision)
294
+
295
+ def test_replace_publish(self):
296
+ self.snippet = DraftStateModel.objects.create(text="Draft-enabled Foo")
297
+ self.initial_revision = self.snippet.save_revision()
298
+ self.snippet.text = "Draft-enabled Foo edited"
299
+ self.edit_revision = self.snippet.save_revision()
300
+ get_response = self.get()
301
+ text_from_revision = get_response.context["form"].initial["text"]
302
+
303
+ timestamp = now()
304
+ with freeze_time(timestamp):
305
+ post_response = self.post(
306
+ post_data={
307
+ "text": text_from_revision + " reverted",
308
+ "revision": self.initial_revision.pk,
309
+ "action-publish": "action-publish",
310
+ }
311
+ )
312
+
313
+ self.assertRedirects(post_response, self.get_url("list", args=[]))
314
+
315
+ self.snippet.refresh_from_db()
316
+ latest_revision = self.snippet.get_latest_revision()
317
+ log_entry = ModelLogEntry.objects.filter(revision=latest_revision).first()
318
+ revert_log_entries = ModelLogEntry.objects.filter(
319
+ content_type=ContentType.objects.get_for_model(DraftStateModel),
320
+ action="wagtail.revert",
321
+ object_id=self.snippet.pk,
322
+ )
323
+
324
+ # The instance should be updated
325
+ self.assertEqual(self.snippet.text, "Draft-enabled Foo reverted")
326
+ # The initial revision, edited revision, and revert revision
327
+ self.assertEqual(self.snippet.revisions.count(), 3)
328
+ # The latest revision should be the revert revision
329
+ self.assertEqual(latest_revision.content["text"], "Draft-enabled Foo reverted")
330
+
331
+ # The latest log entry should use the "wagtail.publish" action
332
+ self.assertIsNotNone(log_entry)
333
+ self.assertEqual(log_entry.action, "wagtail.publish")
334
+
335
+ # There should be a log entry for the revert action
336
+ self.assertEqual(revert_log_entries.count(), 1)
337
+
338
+ # The instance should be live
339
+ self.assertTrue(self.snippet.live)
340
+ self.assertFalse(self.snippet.has_unpublished_changes)
341
+ self.assertEqual(self.snippet.first_published_at, timestamp)
342
+ self.assertEqual(self.snippet.last_published_at, timestamp)
343
+ self.assertEqual(self.snippet.live_revision, self.snippet.latest_revision)
@@ -0,0 +1,112 @@
1
+ from django.core import checks
2
+ from django.test import TestCase
3
+ from django.urls import reverse
4
+
5
+ from wagtail.admin.forms import WagtailAdminModelForm
6
+ from wagtail.admin.panels import FieldPanel, get_edit_handler
7
+ from wagtail.snippets.models import SNIPPET_MODELS, register_snippet
8
+ from wagtail.test.snippets.forms import FancySnippetForm
9
+ from wagtail.test.snippets.models import (
10
+ AlphaSnippet,
11
+ FancySnippet,
12
+ RegisterDecorator,
13
+ RegisterFunction,
14
+ StandardSnippet,
15
+ ZuluSnippet,
16
+ )
17
+ from wagtail.test.testapp.models import AdvertWithTabbedInterface
18
+ from wagtail.test.utils import WagtailTestUtils
19
+
20
+
21
+ class TestModelOrdering(WagtailTestUtils, TestCase):
22
+ def setUp(self):
23
+ for i in range(1, 10):
24
+ AdvertWithTabbedInterface.objects.create(text="advert %d" % i)
25
+ AdvertWithTabbedInterface.objects.create(text="aaaadvert")
26
+ self.login()
27
+
28
+ def test_listing_respects_model_ordering(self):
29
+ response = self.client.get(
30
+ reverse("wagtailsnippets_tests_advertwithtabbedinterface:list")
31
+ )
32
+ self.assertEqual(response.status_code, 200)
33
+ self.assertEqual(response.context["page_obj"][0].text, "aaaadvert")
34
+
35
+ def test_chooser_respects_model_ordering(self):
36
+ response = self.client.get(
37
+ reverse("wagtailsnippetchoosers_tests_advertwithtabbedinterface:choose")
38
+ )
39
+ self.assertEqual(response.status_code, 200)
40
+ self.assertEqual(response.context["results"][0].text, "aaaadvert")
41
+
42
+
43
+ class TestSnippetRegistering(TestCase):
44
+ def test_register_function(self):
45
+ self.assertIn(RegisterFunction, SNIPPET_MODELS)
46
+
47
+ def test_register_decorator(self):
48
+ # Misbehaving decorators often return None
49
+ self.assertIsNotNone(RegisterDecorator)
50
+ self.assertIn(RegisterDecorator, SNIPPET_MODELS)
51
+
52
+
53
+ class TestSnippetOrdering(TestCase):
54
+ def setUp(self):
55
+ register_snippet(ZuluSnippet)
56
+ register_snippet(AlphaSnippet)
57
+
58
+ def test_snippets_ordering(self):
59
+ # Ensure AlphaSnippet is before ZuluSnippet
60
+ # Cannot check first and last position as other snippets
61
+ # may get registered elsewhere during test
62
+ self.assertLess(
63
+ SNIPPET_MODELS.index(AlphaSnippet), SNIPPET_MODELS.index(ZuluSnippet)
64
+ )
65
+
66
+
67
+ class TestSnippetEditHandlers(WagtailTestUtils, TestCase):
68
+ def test_standard_edit_handler(self):
69
+ edit_handler = get_edit_handler(StandardSnippet)
70
+ form_class = edit_handler.get_form_class()
71
+ self.assertTrue(issubclass(form_class, WagtailAdminModelForm))
72
+ self.assertFalse(issubclass(form_class, FancySnippetForm))
73
+
74
+ def test_fancy_edit_handler(self):
75
+ edit_handler = get_edit_handler(FancySnippet)
76
+ form_class = edit_handler.get_form_class()
77
+ self.assertTrue(issubclass(form_class, WagtailAdminModelForm))
78
+ self.assertTrue(issubclass(form_class, FancySnippetForm))
79
+
80
+
81
+ class TestPanelConfigurationChecks(WagtailTestUtils, TestCase):
82
+ def setUp(self):
83
+ self.warning_id = "wagtailadmin.W002"
84
+
85
+ def get_checks_result():
86
+ # run checks only with the 'panels' tag
87
+ checks_result = checks.run_checks(tags=["panels"])
88
+ return [
89
+ warning for warning in checks_result if warning.id == self.warning_id
90
+ ]
91
+
92
+ self.get_checks_result = get_checks_result
93
+
94
+ def test_model_with_single_tabbed_panel_only(self):
95
+ StandardSnippet.content_panels = [FieldPanel("text")]
96
+
97
+ warning = checks.Warning(
98
+ "StandardSnippet.content_panels will have no effect on snippets editing",
99
+ hint="""Ensure that StandardSnippet uses `panels` instead of `content_panels` \
100
+ or set up an `edit_handler` if you want a tabbed editing interface.
101
+ There are no default tabs on non-Page models so there will be no\
102
+ Content tab for the content_panels to render in.""",
103
+ obj=StandardSnippet,
104
+ id="wagtailadmin.W002",
105
+ )
106
+
107
+ checks_results = self.get_checks_result()
108
+
109
+ self.assertEqual([warning], checks_results)
110
+
111
+ # clean up for future checks
112
+ delattr(StandardSnippet, "content_panels")