wagtail 6.4.1__py3-none-any.whl → 7.0__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.
- wagtail/__init__.py +1 -1
- wagtail/actions/copy_page.py +4 -4
- wagtail/admin/action_menu.py +3 -0
- wagtail/admin/active_filters.py +218 -0
- wagtail/admin/auth.py +3 -39
- wagtail/admin/forms/choosers.py +8 -7
- wagtail/admin/forms/models.py +46 -1
- wagtail/admin/forms/pages.py +9 -0
- wagtail/admin/locale/ar/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/ar/LC_MESSAGES/django.po +30 -12
- wagtail/admin/locale/ar/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/ar/LC_MESSAGES/djangojs.po +5 -1
- wagtail/admin/locale/az_AZ/LC_MESSAGES/django.po +1 -1
- wagtail/admin/locale/be/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/be/LC_MESSAGES/django.po +27 -13
- wagtail/admin/locale/be/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/be/LC_MESSAGES/djangojs.po +3 -0
- wagtail/admin/locale/bg/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/bg/LC_MESSAGES/django.po +3 -3
- wagtail/admin/locale/bn/LC_MESSAGES/django.po +1 -1
- wagtail/admin/locale/ca/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/ca/LC_MESSAGES/django.po +76 -22
- wagtail/admin/locale/ca/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/ca/LC_MESSAGES/djangojs.po +3 -0
- wagtail/admin/locale/cs/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/cs/LC_MESSAGES/django.po +30 -19
- wagtail/admin/locale/cs/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/cs/LC_MESSAGES/djangojs.po +5 -2
- wagtail/admin/locale/cy/LC_MESSAGES/django.po +1 -1
- wagtail/admin/locale/cy/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/cy/LC_MESSAGES/djangojs.po +3 -0
- wagtail/admin/locale/da/LC_MESSAGES/django.po +4 -4
- wagtail/admin/locale/de/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/de/LC_MESSAGES/django.po +79 -24
- wagtail/admin/locale/de/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/de/LC_MESSAGES/djangojs.po +5 -1
- wagtail/admin/locale/dv/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/dv/LC_MESSAGES/django.po +7 -18
- wagtail/admin/locale/dv/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/dv/LC_MESSAGES/djangojs.po +3 -0
- wagtail/admin/locale/el/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/el/LC_MESSAGES/django.po +9 -8
- wagtail/admin/locale/el/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/el/LC_MESSAGES/djangojs.po +5 -2
- wagtail/admin/locale/en/LC_MESSAGES/django.po +386 -319
- wagtail/admin/locale/en/LC_MESSAGES/djangojs.po +11 -7
- wagtail/admin/locale/es/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/es/LC_MESSAGES/django.po +32 -22
- wagtail/admin/locale/es/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/es/LC_MESSAGES/djangojs.po +5 -2
- wagtail/admin/locale/es_419/LC_MESSAGES/django.po +2 -1
- wagtail/admin/locale/es_VE/LC_MESSAGES/django.po +2 -1
- wagtail/admin/locale/et/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/et/LC_MESSAGES/django.po +30 -19
- wagtail/admin/locale/et/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/et/LC_MESSAGES/djangojs.po +3 -0
- wagtail/admin/locale/eu/LC_MESSAGES/django.po +1 -1
- wagtail/admin/locale/fa/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/fa/LC_MESSAGES/django.po +10 -9
- wagtail/admin/locale/fa/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/fa/LC_MESSAGES/djangojs.po +5 -2
- wagtail/admin/locale/fi/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/fi/LC_MESSAGES/django.po +30 -21
- wagtail/admin/locale/fi/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/fi/LC_MESSAGES/djangojs.po +5 -2
- wagtail/admin/locale/fr/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/fr/LC_MESSAGES/django.po +80 -24
- wagtail/admin/locale/fr/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/fr/LC_MESSAGES/djangojs.po +3 -0
- wagtail/admin/locale/gl/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/gl/LC_MESSAGES/django.po +75 -22
- wagtail/admin/locale/gl/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/gl/LC_MESSAGES/djangojs.po +3 -0
- wagtail/admin/locale/he_IL/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/he_IL/LC_MESSAGES/django.po +5 -5
- wagtail/admin/locale/hr_HR/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/hr_HR/LC_MESSAGES/django.po +30 -19
- wagtail/admin/locale/hr_HR/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/hr_HR/LC_MESSAGES/djangojs.po +3 -0
- wagtail/admin/locale/ht/LC_MESSAGES/django.po +1 -1
- wagtail/admin/locale/hu/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/hu/LC_MESSAGES/django.po +350 -25
- wagtail/admin/locale/hu/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/hu/LC_MESSAGES/djangojs.po +8 -0
- wagtail/admin/locale/id_ID/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/id_ID/LC_MESSAGES/django.po +11 -11
- wagtail/admin/locale/is_IS/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/is_IS/LC_MESSAGES/django.po +30 -29
- wagtail/admin/locale/is_IS/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/is_IS/LC_MESSAGES/djangojs.po +5 -2
- wagtail/admin/locale/it/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/it/LC_MESSAGES/django.po +30 -29
- wagtail/admin/locale/it/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/it/LC_MESSAGES/djangojs.po +5 -2
- wagtail/admin/locale/ja/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/ja/LC_MESSAGES/django.po +14 -15
- wagtail/admin/locale/ja/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/ja/LC_MESSAGES/djangojs.po +5 -2
- wagtail/admin/locale/ka/LC_MESSAGES/django.po +1 -1
- wagtail/admin/locale/ko/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/ko/LC_MESSAGES/django.po +92 -19
- wagtail/admin/locale/ko/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/ko/LC_MESSAGES/djangojs.po +16 -0
- wagtail/admin/locale/lt/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/lt/LC_MESSAGES/django.po +30 -15
- wagtail/admin/locale/lt/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/lt/LC_MESSAGES/djangojs.po +3 -0
- wagtail/admin/locale/lv/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/lv/LC_MESSAGES/django.po +10 -9
- wagtail/admin/locale/lv/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/lv/LC_MESSAGES/djangojs.po +3 -0
- wagtail/admin/locale/mi/LC_MESSAGES/django.po +1 -1
- wagtail/admin/locale/mi/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/mi/LC_MESSAGES/djangojs.po +3 -0
- wagtail/admin/locale/mn/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/mn/LC_MESSAGES/django.po +12 -11
- wagtail/admin/locale/my/LC_MESSAGES/django.po +1 -1
- wagtail/admin/locale/nb/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/nb/LC_MESSAGES/django.po +31 -15
- wagtail/admin/locale/nb/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/nb/LC_MESSAGES/djangojs.po +5 -2
- wagtail/admin/locale/nl/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/nl/LC_MESSAGES/django.po +47 -25
- wagtail/admin/locale/nl/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/nl/LC_MESSAGES/djangojs.po +5 -2
- wagtail/admin/locale/pl/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/pl/LC_MESSAGES/django.po +30 -22
- wagtail/admin/locale/pl/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/pl/LC_MESSAGES/djangojs.po +3 -0
- wagtail/admin/locale/pt_BR/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/pt_BR/LC_MESSAGES/django.po +33 -28
- wagtail/admin/locale/pt_BR/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/pt_BR/LC_MESSAGES/djangojs.po +3 -0
- wagtail/admin/locale/pt_PT/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/pt_PT/LC_MESSAGES/django.po +31 -18
- wagtail/admin/locale/pt_PT/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/pt_PT/LC_MESSAGES/djangojs.po +5 -2
- wagtail/admin/locale/ro/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/ro/LC_MESSAGES/django.po +30 -28
- wagtail/admin/locale/ro/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/ro/LC_MESSAGES/djangojs.po +5 -2
- wagtail/admin/locale/ru/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/ru/LC_MESSAGES/django.po +34 -29
- wagtail/admin/locale/ru/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/ru/LC_MESSAGES/djangojs.po +5 -2
- wagtail/admin/locale/sk_SK/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/sk_SK/LC_MESSAGES/django.po +6 -6
- wagtail/admin/locale/sk_SK/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/sk_SK/LC_MESSAGES/djangojs.po +3 -0
- wagtail/admin/locale/sl/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/sl/LC_MESSAGES/django.po +31 -27
- wagtail/admin/locale/sl/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/sl/LC_MESSAGES/djangojs.po +3 -0
- wagtail/admin/locale/sv/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/sv/LC_MESSAGES/django.po +30 -18
- wagtail/admin/locale/sv/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/sv/LC_MESSAGES/djangojs.po +5 -2
- wagtail/admin/locale/tet/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/tet/LC_MESSAGES/django.po +5 -5
- wagtail/admin/locale/tet/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/tet/LC_MESSAGES/djangojs.po +5 -2
- wagtail/admin/locale/th/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/th/LC_MESSAGES/django.po +10 -17
- wagtail/admin/locale/tr/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/tr/LC_MESSAGES/django.po +28 -13
- wagtail/admin/locale/tr/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/tr/LC_MESSAGES/djangojs.po +5 -2
- wagtail/admin/locale/tr_TR/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/tr_TR/LC_MESSAGES/django.po +28 -13
- wagtail/admin/locale/tr_TR/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/tr_TR/LC_MESSAGES/djangojs.po +5 -2
- wagtail/admin/locale/ug/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/ug/LC_MESSAGES/django.po +12 -29
- wagtail/admin/locale/ug/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/ug/LC_MESSAGES/djangojs.po +3 -0
- wagtail/admin/locale/uk/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/uk/LC_MESSAGES/django.po +30 -26
- wagtail/admin/locale/uk/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/uk/LC_MESSAGES/djangojs.po +6 -3
- wagtail/admin/locale/vi/LC_MESSAGES/django.po +2 -1
- wagtail/admin/locale/zh/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/zh/LC_MESSAGES/django.po +2 -2
- wagtail/admin/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/zh_Hans/LC_MESSAGES/django.po +33 -18
- wagtail/admin/locale/zh_Hans/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/zh_Hans/LC_MESSAGES/djangojs.po +5 -2
- wagtail/admin/locale/zh_Hant/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/zh_Hant/LC_MESSAGES/django.po +11 -14
- wagtail/admin/locale/zh_Hant/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/zh_Hant/LC_MESSAGES/djangojs.po +3 -0
- wagtail/admin/localization.py +42 -1
- wagtail/admin/menu.py +1 -10
- wagtail/admin/paginator.py +95 -0
- wagtail/admin/panels/field_panel.py +27 -6
- wagtail/admin/panels/inline_panel.py +28 -11
- wagtail/admin/panels/multiple_chooser_panel.py +3 -3
- wagtail/admin/rich_text/converters/html_to_contentstate.py +6 -6
- wagtail/admin/search.py +1 -10
- wagtail/admin/static/wagtailadmin/css/core.css +1 -1
- wagtail/admin/static/wagtailadmin/css/panels/draftail.css +1 -1
- wagtail/admin/static/wagtailadmin/js/bulk-actions.js +1 -1
- wagtail/admin/static/wagtailadmin/js/comments.js +1 -1
- wagtail/admin/static/wagtailadmin/js/core.js +1 -1
- wagtail/admin/static/wagtailadmin/js/draftail.js +1 -1
- wagtail/admin/static/wagtailadmin/js/sidebar.js +1 -1
- wagtail/admin/static/wagtailadmin/js/telepath/blocks.js +1 -1
- wagtail/admin/static/wagtailadmin/js/telepath/widgets.js +1 -1
- wagtail/admin/static/wagtailadmin/js/vendor.js +1 -1
- wagtail/admin/static/wagtailadmin/js/vendor.js.LICENSE.txt +32 -11
- wagtail/admin/static/wagtailadmin/js/workflow-action.js +1 -1
- wagtail/admin/templates/wagtailadmin/admin_base.html +1 -0
- wagtail/admin/templates/wagtailadmin/generic/index.html +7 -2
- wagtail/admin/templates/wagtailadmin/pages/_editor_js.html +2 -3
- wagtail/admin/templates/wagtailadmin/pages/action_menu/publish.html +2 -2
- wagtail/admin/templates/wagtailadmin/pages/bulk_actions/confirm_bulk_move.html +0 -1
- wagtail/admin/templates/wagtailadmin/pages/move_choose_destination.html +0 -1
- wagtail/admin/templates/wagtailadmin/panels/publishing/schedule_publishing_panel.html +1 -1
- wagtail/admin/templates/wagtailadmin/shared/active_filters.html +8 -1
- wagtail/admin/templates/wagtailadmin/shared/pagination_nav.html +25 -13
- wagtail/admin/templates/wagtailadmin/shared/side_panels/includes/status/privacy.html +1 -2
- wagtail/admin/templates/wagtailadmin/shared/side_panels/includes/status/workflow.html +1 -1
- wagtail/admin/templates/wagtailadmin/skeleton.html +2 -1
- wagtail/admin/templates/wagtailadmin/tables/locale_cell.html +4 -0
- wagtail/admin/templates/wagtailadmin/userbar/base.html +10 -8
- wagtail/admin/templatetags/wagtailadmin_tags.py +57 -132
- wagtail/admin/tests/pages/test_bulk_actions/test_bulk_move.py +31 -0
- wagtail/admin/tests/pages/test_create_page.py +627 -0
- wagtail/admin/tests/pages/test_edit_page.py +102 -18
- wagtail/admin/tests/pages/test_explorer_view.py +55 -26
- wagtail/admin/tests/pages/test_page_search.py +10 -0
- wagtail/admin/tests/pages/test_page_usage.py +2 -1
- wagtail/admin/tests/pages/test_preview.py +176 -36
- wagtail/admin/tests/pages/test_revisions.py +4 -7
- wagtail/admin/tests/test_account_management.py +1 -1
- wagtail/admin/tests/test_admin_search.py +1 -0
- wagtail/admin/tests/test_buttons_hooks.py +9 -145
- wagtail/admin/tests/test_collections_views.py +60 -0
- wagtail/admin/tests/test_contentstate.py +1 -1
- wagtail/admin/tests/test_dashboard.py +25 -0
- wagtail/admin/tests/test_edit_handlers.py +64 -0
- wagtail/admin/tests/test_forms.py +38 -0
- wagtail/admin/tests/test_keyboard_shortcuts.py +141 -5
- wagtail/admin/tests/test_page_chooser.py +2 -2
- wagtail/admin/tests/test_paginator.py +86 -0
- wagtail/admin/tests/test_privacy.py +12 -1
- wagtail/admin/tests/test_reports_views.py +8 -43
- wagtail/admin/tests/test_rich_text.py +14 -0
- wagtail/admin/tests/test_templatetags.py +50 -36
- wagtail/admin/tests/test_userbar.py +79 -15
- wagtail/admin/tests/test_views.py +16 -1
- wagtail/admin/tests/test_widgets.py +47 -8
- wagtail/admin/tests/test_workflows.py +119 -6
- wagtail/admin/tests/tests.py +16 -15
- wagtail/admin/tests/ui/test_sidebar.py +0 -27
- wagtail/admin/tests/viewsets/test_chooser_viewset.py +38 -4
- wagtail/admin/tests/viewsets/test_model_viewset.py +1 -37
- wagtail/admin/ui/side_panels.py +14 -16
- wagtail/admin/ui/sidebar.py +5 -37
- wagtail/admin/ui/tables/__init__.py +34 -2
- wagtail/admin/ui/tables/pages.py +9 -0
- wagtail/admin/urls/__init__.py +2 -2
- wagtail/admin/urls/pages.py +3 -1
- wagtail/admin/userbar.py +3 -5
- wagtail/admin/utils.py +43 -4
- wagtail/admin/views/generic/base.py +16 -115
- wagtail/admin/views/generic/chooser.py +25 -8
- wagtail/admin/views/generic/mixins.py +45 -11
- wagtail/admin/views/generic/models.py +15 -30
- wagtail/admin/views/generic/preview.py +9 -3
- wagtail/admin/views/generic/workflow.py +11 -1
- wagtail/admin/views/i18n.py +14 -0
- wagtail/admin/views/pages/bulk_actions/move.py +1 -1
- wagtail/admin/views/pages/create.py +68 -6
- wagtail/admin/views/pages/edit.py +33 -14
- wagtail/admin/views/pages/listing.py +3 -2
- wagtail/admin/views/pages/move.py +43 -29
- wagtail/admin/views/pages/preview.py +15 -2
- wagtail/admin/views/pages/search.py +3 -0
- wagtail/admin/views/reports/base.py +0 -22
- wagtail/admin/views/reports/page_types_usage.py +2 -1
- wagtail/admin/views/tags.py +3 -1
- wagtail/admin/views/workflows.py +0 -3
- wagtail/admin/viewsets/model.py +5 -48
- wagtail/admin/wagtail_hooks.py +16 -17
- wagtail/admin/widgets/button.py +3 -33
- wagtail/admin/widgets/tags.py +31 -3
- wagtail/api/v2/views.py +4 -4
- wagtail/blocks/base.py +18 -15
- wagtail/blocks/field_block.py +2 -1
- wagtail/blocks/stream_block.py +15 -11
- wagtail/compat.py +17 -1
- wagtail/contrib/forms/forms.py +64 -31
- wagtail/contrib/forms/locale/af/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/forms/locale/ar/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/ar/LC_MESSAGES/django.po +1 -7
- wagtail/contrib/forms/locale/az_AZ/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/forms/locale/be/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/be/LC_MESSAGES/django.po +1 -7
- wagtail/contrib/forms/locale/bg/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/forms/locale/bn/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/forms/locale/ca/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/ca/LC_MESSAGES/django.po +15 -6
- wagtail/contrib/forms/locale/cs/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/cs/LC_MESSAGES/django.po +2 -9
- wagtail/contrib/forms/locale/cy/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/cy/LC_MESSAGES/django.po +2 -7
- wagtail/contrib/forms/locale/da/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/forms/locale/de/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/de/LC_MESSAGES/django.po +3 -10
- wagtail/contrib/forms/locale/dv/LC_MESSAGES/django.po +2 -1
- wagtail/contrib/forms/locale/el/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/el/LC_MESSAGES/django.po +1 -9
- wagtail/contrib/forms/locale/en/LC_MESSAGES/django.po +13 -6
- wagtail/contrib/forms/locale/es/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/es/LC_MESSAGES/django.po +1 -9
- wagtail/contrib/forms/locale/es_419/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/forms/locale/et/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/et/LC_MESSAGES/django.po +1 -7
- wagtail/contrib/forms/locale/eu/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/forms/locale/fa/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/fa/LC_MESSAGES/django.po +1 -7
- wagtail/contrib/forms/locale/fi/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/fi/LC_MESSAGES/django.po +1 -9
- wagtail/contrib/forms/locale/fr/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/fr/LC_MESSAGES/django.po +8 -6
- wagtail/contrib/forms/locale/gl/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/gl/LC_MESSAGES/django.po +17 -6
- wagtail/contrib/forms/locale/he_IL/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/forms/locale/hr_HR/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/hr_HR/LC_MESSAGES/django.po +1 -9
- wagtail/contrib/forms/locale/ht/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/forms/locale/hu/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/hu/LC_MESSAGES/django.po +1 -9
- wagtail/contrib/forms/locale/hy/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/forms/locale/id_ID/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/id_ID/LC_MESSAGES/django.po +1 -9
- wagtail/contrib/forms/locale/is_IS/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/is_IS/LC_MESSAGES/django.po +1 -9
- wagtail/contrib/forms/locale/it/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/it/LC_MESSAGES/django.po +1 -7
- wagtail/contrib/forms/locale/ja/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/ja/LC_MESSAGES/django.po +1 -9
- wagtail/contrib/forms/locale/ka/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/forms/locale/ko/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/ko/LC_MESSAGES/django.po +1 -9
- wagtail/contrib/forms/locale/lt/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/lt/LC_MESSAGES/django.po +1 -8
- wagtail/contrib/forms/locale/lv/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/forms/locale/mi/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/forms/locale/mn/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/mn/LC_MESSAGES/django.po +3 -2
- wagtail/contrib/forms/locale/my/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/my/LC_MESSAGES/django.po +1 -7
- wagtail/contrib/forms/locale/nb/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/nb/LC_MESSAGES/django.po +1 -8
- wagtail/contrib/forms/locale/nl/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/nl/LC_MESSAGES/django.po +2 -10
- wagtail/contrib/forms/locale/pl/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/pl/LC_MESSAGES/django.po +2 -8
- wagtail/contrib/forms/locale/pt_BR/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/pt_BR/LC_MESSAGES/django.po +2 -8
- wagtail/contrib/forms/locale/pt_PT/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/pt_PT/LC_MESSAGES/django.po +1 -8
- wagtail/contrib/forms/locale/ro/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/ro/LC_MESSAGES/django.po +2 -8
- wagtail/contrib/forms/locale/ru/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/ru/LC_MESSAGES/django.po +2 -7
- wagtail/contrib/forms/locale/sk_SK/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/sk_SK/LC_MESSAGES/django.po +1 -8
- wagtail/contrib/forms/locale/sl/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/sl/LC_MESSAGES/django.po +2 -8
- wagtail/contrib/forms/locale/sv/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/sv/LC_MESSAGES/django.po +1 -9
- wagtail/contrib/forms/locale/tet/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/tet/LC_MESSAGES/django.po +1 -9
- wagtail/contrib/forms/locale/th/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/th/LC_MESSAGES/django.po +1 -7
- wagtail/contrib/forms/locale/tr/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/tr/LC_MESSAGES/django.po +1 -7
- wagtail/contrib/forms/locale/tr_TR/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/tr_TR/LC_MESSAGES/django.po +1 -7
- wagtail/contrib/forms/locale/ug/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/ug/LC_MESSAGES/django.po +1 -7
- wagtail/contrib/forms/locale/uk/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/uk/LC_MESSAGES/django.po +1 -7
- wagtail/contrib/forms/locale/vi/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/forms/locale/zh/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/forms/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/zh_Hans/LC_MESSAGES/django.po +2 -8
- wagtail/contrib/forms/locale/zh_Hant/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/zh_Hant/LC_MESSAGES/django.po +1 -7
- wagtail/contrib/forms/tests/test_forms.py +62 -0
- wagtail/contrib/forms/tests/test_views.py +161 -5
- wagtail/contrib/frontend_cache/backends/cloudfront.py +1 -29
- wagtail/contrib/frontend_cache/tests.py +112 -52
- wagtail/contrib/frontend_cache/utils.py +109 -17
- wagtail/contrib/redirects/forms.py +3 -2
- wagtail/contrib/redirects/locale/ar/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/be/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/bg/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/bn/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/ca/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/redirects/locale/ca/LC_MESSAGES/django.po +2 -2
- wagtail/contrib/redirects/locale/cs/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/cy/LC_MESSAGES/django.po +2 -1
- wagtail/contrib/redirects/locale/da/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/de/LC_MESSAGES/django.po +2 -1
- wagtail/contrib/redirects/locale/dv/LC_MESSAGES/django.po +2 -1
- wagtail/contrib/redirects/locale/el/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/en/LC_MESSAGES/django.po +8 -8
- wagtail/contrib/redirects/locale/en_IN/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/es/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/es_419/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/es_VE/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/et/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/eu/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/fa/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/redirects/locale/fa/LC_MESSAGES/django.po +3 -2
- wagtail/contrib/redirects/locale/fi/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/redirects/locale/fi/LC_MESSAGES/django.po +2 -2
- wagtail/contrib/redirects/locale/fr/LC_MESSAGES/django.po +3 -1
- wagtail/contrib/redirects/locale/gl/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/he_IL/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/hi/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/hr_HR/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/ht/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/hu/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/hy/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/id_ID/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/is_IS/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/it/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/ja/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/ka/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/ko/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/redirects/locale/ko/LC_MESSAGES/django.po +18 -2
- wagtail/contrib/redirects/locale/lt/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/lv/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/mi/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/mn/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/my/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/nb/LC_MESSAGES/django.po +2 -1
- wagtail/contrib/redirects/locale/nl/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/redirects/locale/nl/LC_MESSAGES/django.po +2 -2
- wagtail/contrib/redirects/locale/pl/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/redirects/locale/pl/LC_MESSAGES/django.po +2 -2
- wagtail/contrib/redirects/locale/pt_BR/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/redirects/locale/pt_BR/LC_MESSAGES/django.po +3 -2
- wagtail/contrib/redirects/locale/pt_PT/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/ro/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/redirects/locale/ro/LC_MESSAGES/django.po +3 -2
- wagtail/contrib/redirects/locale/ru/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/redirects/locale/ru/LC_MESSAGES/django.po +3 -2
- wagtail/contrib/redirects/locale/sk_SK/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/sl/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/redirects/locale/sl/LC_MESSAGES/django.po +2 -2
- wagtail/contrib/redirects/locale/sr/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/sv/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/redirects/locale/sv/LC_MESSAGES/django.po +2 -2
- wagtail/contrib/redirects/locale/ta/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/tet/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/th/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/tr/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/tr_TR/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/ug/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/uk/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/redirects/locale/uk/LC_MESSAGES/django.po +3 -2
- wagtail/contrib/redirects/locale/vi/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/zh/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/redirects/locale/zh_Hans/LC_MESSAGES/django.po +2 -2
- wagtail/contrib/redirects/locale/zh_Hant/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/redirects/tmp_storages.py +1 -0
- wagtail/contrib/routable_page/tests.py +3 -3
- wagtail/contrib/search_promotions/forms.py +3 -2
- wagtail/contrib/search_promotions/locale/af/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/search_promotions/locale/ar/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/ar/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/az_AZ/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/search_promotions/locale/be/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/be/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/bg/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/bg/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/bn/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/search_promotions/locale/ca/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/ca/LC_MESSAGES/django.po +3 -6
- wagtail/contrib/search_promotions/locale/cs/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/cs/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/cy/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/cy/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/da/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/search_promotions/locale/de/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/de/LC_MESSAGES/django.po +3 -6
- wagtail/contrib/search_promotions/locale/dv/LC_MESSAGES/django.po +2 -1
- wagtail/contrib/search_promotions/locale/el/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/el/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/en/LC_MESSAGES/django.po +19 -23
- wagtail/contrib/search_promotions/locale/es/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/es/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/es_419/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/search_promotions/locale/et/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/et/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/eu/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/search_promotions/locale/fa/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/fa/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/fi/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/fi/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/fr/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/fr/LC_MESSAGES/django.po +3 -6
- wagtail/contrib/search_promotions/locale/gl/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/gl/LC_MESSAGES/django.po +3 -6
- wagtail/contrib/search_promotions/locale/he_IL/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/search_promotions/locale/hr_HR/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/hr_HR/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/ht/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/ht/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/hu/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/hu/LC_MESSAGES/django.po +11 -5
- wagtail/contrib/search_promotions/locale/id_ID/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/id_ID/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/is_IS/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/is_IS/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/it/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/it/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/ja/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/ja/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/ka/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/search_promotions/locale/ko/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/ko/LC_MESSAGES/django.po +5 -4
- wagtail/contrib/search_promotions/locale/lt/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/lt/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/lv/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/lv/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/mi/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/mi/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/mn/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/mn/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/my/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/my/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/nb/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/nb/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/nl/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/nl/LC_MESSAGES/django.po +3 -6
- wagtail/contrib/search_promotions/locale/pl/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/pl/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/pt_BR/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/pt_BR/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/pt_PT/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/pt_PT/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/ro/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/ro/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/ru/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/ru/LC_MESSAGES/django.po +3 -6
- wagtail/contrib/search_promotions/locale/sk_SK/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/sk_SK/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/sl/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/sl/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/sv/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/sv/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/tet/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/tet/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/th/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/th/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/tr/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/tr/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/tr_TR/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/tr_TR/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/ug/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/ug/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/uk/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/uk/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/vi/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/vi/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/zh/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/zh/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/zh_Hans/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/locale/zh_Hant/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/zh_Hant/LC_MESSAGES/django.po +1 -4
- wagtail/contrib/search_promotions/models.py +1 -7
- wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/queries/chooser/results.html +1 -1
- wagtail/contrib/search_promotions/tests.py +4 -5
- wagtail/contrib/search_promotions/views/settings.py +1 -2
- wagtail/contrib/settings/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/settings/registry.py +1 -11
- wagtail/contrib/simple_translation/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/simple_translation/locale/hu/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/simple_translation/locale/hu/LC_MESSAGES/django.po +5 -2
- wagtail/contrib/styleguide/locale/af/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/ar/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/az_AZ/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/be/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/styleguide/locale/be/LC_MESSAGES/django.po +3 -2
- wagtail/contrib/styleguide/locale/bg/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/bn/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/ca/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/cs/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/cy/LC_MESSAGES/django.po +2 -1
- wagtail/contrib/styleguide/locale/da/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/de/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/dv/LC_MESSAGES/django.po +2 -1
- wagtail/contrib/styleguide/locale/el/LC_MESSAGES/django.po +2 -1
- wagtail/contrib/styleguide/locale/en/LC_MESSAGES/django.po +7 -7
- wagtail/contrib/styleguide/locale/en_IN/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/es/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/es_419/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/es_VE/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/et/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/fa/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/fi/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/fr/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/gl/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/he_IL/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/hi/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/hr_HR/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/ht/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/hu/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/id_ID/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/is_IS/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/it/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/ja/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/ka/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/ko/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/lt/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/lv/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/mi/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/mn/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/my/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/nb/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/nl/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/pl/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/pt_BR/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/pt_PT/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/ro/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/ru/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/sk_SK/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/styleguide/locale/sk_SK/LC_MESSAGES/django.po +2 -2
- wagtail/contrib/styleguide/locale/sl/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/sv/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/ta/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/tet/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/th/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/tr/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/tr_TR/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/ug/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/uk/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/vi/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/zh/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/zh_Hans/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/locale/zh_Hant/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/styleguide/views.py +2 -1
- wagtail/contrib/table_block/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/typed_table_block/blocks.py +5 -0
- wagtail/contrib/typed_table_block/locale/ar/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/typed_table_block/locale/be/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/typed_table_block/locale/ca/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/typed_table_block/locale/cy/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/typed_table_block/locale/de/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/typed_table_block/locale/dv/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/typed_table_block/locale/el/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/typed_table_block/locale/en/LC_MESSAGES/django.po +10 -10
- wagtail/contrib/typed_table_block/locale/es/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/typed_table_block/locale/fa/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/typed_table_block/locale/fi/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/typed_table_block/locale/fr/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/typed_table_block/locale/gl/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/typed_table_block/locale/hu/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/typed_table_block/locale/is_IS/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/typed_table_block/locale/it/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/typed_table_block/locale/ko/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/typed_table_block/locale/lt/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/typed_table_block/locale/nl/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/typed_table_block/locale/pl/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/typed_table_block/locale/pt_BR/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/typed_table_block/locale/pt_PT/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/typed_table_block/locale/ro/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/typed_table_block/locale/ru/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/typed_table_block/locale/sl/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/typed_table_block/locale/sv/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/typed_table_block/locale/th/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/typed_table_block/locale/ug/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/typed_table_block/locale/uk/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/typed_table_block/locale/zh/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/typed_table_block/locale/zh_Hans/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/typed_table_block/locale/zh_Hant/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/typed_table_block/tests.py +27 -0
- wagtail/coreutils.py +0 -19
- wagtail/documents/locale/af/LC_MESSAGES/django.po +1 -1
- wagtail/documents/locale/ar/LC_MESSAGES/django.mo +0 -0
- wagtail/documents/locale/ar/LC_MESSAGES/django.po +3 -2
- wagtail/documents/locale/az_AZ/LC_MESSAGES/django.po +1 -1
- wagtail/documents/locale/be/LC_MESSAGES/django.po +1 -1
- wagtail/documents/locale/bg/LC_MESSAGES/django.po +1 -1
- wagtail/documents/locale/bn/LC_MESSAGES/django.po +1 -1
- wagtail/documents/locale/ca/LC_MESSAGES/django.mo +0 -0
- wagtail/documents/locale/ca/LC_MESSAGES/django.po +2 -2
- wagtail/documents/locale/cs/LC_MESSAGES/django.po +1 -1
- wagtail/documents/locale/cy/LC_MESSAGES/django.po +1 -1
- wagtail/documents/locale/da/LC_MESSAGES/django.po +1 -1
- wagtail/documents/locale/de/LC_MESSAGES/django.mo +0 -0
- wagtail/documents/locale/de/LC_MESSAGES/django.po +3 -3
- wagtail/documents/locale/dv/LC_MESSAGES/django.po +2 -1
- wagtail/documents/locale/el/LC_MESSAGES/django.po +2 -1
- wagtail/documents/locale/en/LC_MESSAGES/django.po +4 -4
- wagtail/documents/locale/es/LC_MESSAGES/django.mo +0 -0
- wagtail/documents/locale/es/LC_MESSAGES/django.po +3 -2
- wagtail/documents/locale/es_419/LC_MESSAGES/django.po +1 -1
- wagtail/documents/locale/et/LC_MESSAGES/django.po +1 -1
- wagtail/documents/locale/eu/LC_MESSAGES/django.po +1 -1
- wagtail/documents/locale/fa/LC_MESSAGES/django.po +1 -1
- wagtail/documents/locale/fi/LC_MESSAGES/django.mo +0 -0
- wagtail/documents/locale/fi/LC_MESSAGES/django.po +2 -2
- wagtail/documents/locale/fr/LC_MESSAGES/django.po +2 -1
- wagtail/documents/locale/gl/LC_MESSAGES/django.po +1 -1
- wagtail/documents/locale/he_IL/LC_MESSAGES/django.po +1 -1
- wagtail/documents/locale/hr_HR/LC_MESSAGES/django.po +1 -1
- wagtail/documents/locale/ht/LC_MESSAGES/django.po +1 -1
- wagtail/documents/locale/hu/LC_MESSAGES/django.mo +0 -0
- wagtail/documents/locale/hu/LC_MESSAGES/django.po +18 -3
- wagtail/documents/locale/id_ID/LC_MESSAGES/django.po +1 -1
- wagtail/documents/locale/is_IS/LC_MESSAGES/django.po +1 -1
- wagtail/documents/locale/it/LC_MESSAGES/django.mo +0 -0
- wagtail/documents/locale/it/LC_MESSAGES/django.po +2 -2
- wagtail/documents/locale/ja/LC_MESSAGES/django.po +1 -1
- wagtail/documents/locale/ka/LC_MESSAGES/django.po +1 -1
- wagtail/documents/locale/ko/LC_MESSAGES/django.mo +0 -0
- wagtail/documents/locale/ko/LC_MESSAGES/django.po +14 -2
- wagtail/documents/locale/lt/LC_MESSAGES/django.po +1 -1
- wagtail/documents/locale/lv/LC_MESSAGES/django.po +1 -1
- wagtail/documents/locale/mi/LC_MESSAGES/django.po +1 -1
- wagtail/documents/locale/mn/LC_MESSAGES/django.po +1 -1
- wagtail/documents/locale/my/LC_MESSAGES/django.po +1 -1
- wagtail/documents/locale/nb/LC_MESSAGES/django.mo +0 -0
- wagtail/documents/locale/nb/LC_MESSAGES/django.po +3 -2
- wagtail/documents/locale/nl/LC_MESSAGES/django.mo +0 -0
- wagtail/documents/locale/nl/LC_MESSAGES/django.po +2 -2
- wagtail/documents/locale/pl/LC_MESSAGES/django.mo +0 -0
- wagtail/documents/locale/pl/LC_MESSAGES/django.po +2 -2
- wagtail/documents/locale/pt_BR/LC_MESSAGES/django.mo +0 -0
- wagtail/documents/locale/pt_BR/LC_MESSAGES/django.po +2 -2
- wagtail/documents/locale/pt_PT/LC_MESSAGES/django.po +1 -1
- wagtail/documents/locale/ro/LC_MESSAGES/django.mo +0 -0
- wagtail/documents/locale/ro/LC_MESSAGES/django.po +2 -2
- wagtail/documents/locale/ru/LC_MESSAGES/django.mo +0 -0
- wagtail/documents/locale/ru/LC_MESSAGES/django.po +5 -2
- wagtail/documents/locale/sk_SK/LC_MESSAGES/django.mo +0 -0
- wagtail/documents/locale/sk_SK/LC_MESSAGES/django.po +2 -2
- wagtail/documents/locale/sl/LC_MESSAGES/django.mo +0 -0
- wagtail/documents/locale/sl/LC_MESSAGES/django.po +2 -2
- wagtail/documents/locale/sv/LC_MESSAGES/django.po +1 -1
- wagtail/documents/locale/tet/LC_MESSAGES/django.po +1 -1
- wagtail/documents/locale/th/LC_MESSAGES/django.po +1 -1
- wagtail/documents/locale/tr/LC_MESSAGES/django.po +1 -1
- wagtail/documents/locale/tr_TR/LC_MESSAGES/django.po +1 -1
- wagtail/documents/locale/ug/LC_MESSAGES/django.po +1 -1
- wagtail/documents/locale/uk/LC_MESSAGES/django.po +1 -1
- wagtail/documents/locale/vi/LC_MESSAGES/django.po +1 -1
- wagtail/documents/locale/zh/LC_MESSAGES/django.po +1 -1
- wagtail/documents/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
- wagtail/documents/locale/zh_Hans/LC_MESSAGES/django.po +3 -2
- wagtail/documents/locale/zh_Hant/LC_MESSAGES/django.po +1 -1
- wagtail/documents/rich_text/contentstate.py +1 -0
- wagtail/documents/rich_text/editor_html.py +1 -0
- wagtail/documents/tests/test_collection_privacy.py +1 -1
- wagtail/documents/tests/test_serializers.py +0 -1
- wagtail/documents/views/serve.py +0 -15
- wagtail/documents/wagtail_hooks.py +2 -17
- wagtail/embeds/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/embeds/rich_text/contentstate.py +1 -0
- wagtail/embeds/rich_text/editor_html.py +1 -0
- wagtail/embeds/wagtail_hooks.py +1 -1
- wagtail/images/formats.py +0 -11
- wagtail/images/locale/ar/LC_MESSAGES/django.po +1 -1
- wagtail/images/locale/az_AZ/LC_MESSAGES/django.po +1 -1
- wagtail/images/locale/be/LC_MESSAGES/django.po +1 -1
- wagtail/images/locale/bg/LC_MESSAGES/django.po +1 -1
- wagtail/images/locale/bn/LC_MESSAGES/django.po +1 -1
- wagtail/images/locale/ca/LC_MESSAGES/django.mo +0 -0
- wagtail/images/locale/ca/LC_MESSAGES/django.po +2 -2
- wagtail/images/locale/cs/LC_MESSAGES/django.mo +0 -0
- wagtail/images/locale/cs/LC_MESSAGES/django.po +3 -2
- wagtail/images/locale/cy/LC_MESSAGES/django.po +2 -1
- wagtail/images/locale/da/LC_MESSAGES/django.po +1 -1
- wagtail/images/locale/de/LC_MESSAGES/django.mo +0 -0
- wagtail/images/locale/de/LC_MESSAGES/django.po +3 -2
- wagtail/images/locale/dv/LC_MESSAGES/django.po +2 -1
- wagtail/images/locale/el/LC_MESSAGES/django.po +1 -1
- wagtail/images/locale/en/LC_MESSAGES/django.po +4 -4
- wagtail/images/locale/es/LC_MESSAGES/django.po +1 -1
- wagtail/images/locale/es_419/LC_MESSAGES/django.po +1 -1
- wagtail/images/locale/et/LC_MESSAGES/django.po +1 -1
- wagtail/images/locale/eu/LC_MESSAGES/django.po +1 -1
- wagtail/images/locale/fa/LC_MESSAGES/django.mo +0 -0
- wagtail/images/locale/fa/LC_MESSAGES/django.po +3 -2
- wagtail/images/locale/fi/LC_MESSAGES/django.mo +0 -0
- wagtail/images/locale/fi/LC_MESSAGES/django.po +2 -2
- wagtail/images/locale/fr/LC_MESSAGES/django.mo +0 -0
- wagtail/images/locale/fr/LC_MESSAGES/django.po +3 -2
- wagtail/images/locale/gl/LC_MESSAGES/django.mo +0 -0
- wagtail/images/locale/gl/LC_MESSAGES/django.po +2 -2
- wagtail/images/locale/he_IL/LC_MESSAGES/django.po +1 -1
- wagtail/images/locale/hr_HR/LC_MESSAGES/django.po +1 -1
- wagtail/images/locale/ht/LC_MESSAGES/django.po +1 -1
- wagtail/images/locale/hu/LC_MESSAGES/django.mo +0 -0
- wagtail/images/locale/hu/LC_MESSAGES/django.po +23 -3
- wagtail/images/locale/id_ID/LC_MESSAGES/django.po +1 -1
- wagtail/images/locale/is_IS/LC_MESSAGES/django.po +1 -1
- wagtail/images/locale/it/LC_MESSAGES/django.mo +0 -0
- wagtail/images/locale/it/LC_MESSAGES/django.po +2 -2
- wagtail/images/locale/ja/LC_MESSAGES/django.po +1 -1
- wagtail/images/locale/ka/LC_MESSAGES/django.po +1 -1
- wagtail/images/locale/ko/LC_MESSAGES/django.mo +0 -0
- wagtail/images/locale/ko/LC_MESSAGES/django.po +31 -2
- wagtail/images/locale/lt/LC_MESSAGES/django.po +1 -1
- wagtail/images/locale/lv/LC_MESSAGES/django.po +1 -1
- wagtail/images/locale/mi/LC_MESSAGES/django.po +1 -1
- wagtail/images/locale/mn/LC_MESSAGES/django.mo +0 -0
- wagtail/images/locale/mn/LC_MESSAGES/django.po +3 -2
- wagtail/images/locale/my/LC_MESSAGES/django.po +1 -1
- wagtail/images/locale/nb/LC_MESSAGES/django.mo +0 -0
- wagtail/images/locale/nb/LC_MESSAGES/django.po +3 -2
- wagtail/images/locale/nl/LC_MESSAGES/django.mo +0 -0
- wagtail/images/locale/nl/LC_MESSAGES/django.po +4 -5
- wagtail/images/locale/pl/LC_MESSAGES/django.mo +0 -0
- wagtail/images/locale/pl/LC_MESSAGES/django.po +2 -2
- wagtail/images/locale/pt_BR/LC_MESSAGES/django.mo +0 -0
- wagtail/images/locale/pt_BR/LC_MESSAGES/django.po +2 -2
- wagtail/images/locale/pt_PT/LC_MESSAGES/django.po +1 -1
- wagtail/images/locale/ro/LC_MESSAGES/django.mo +0 -0
- wagtail/images/locale/ro/LC_MESSAGES/django.po +3 -2
- wagtail/images/locale/ru/LC_MESSAGES/django.mo +0 -0
- wagtail/images/locale/ru/LC_MESSAGES/django.po +5 -2
- wagtail/images/locale/sk_SK/LC_MESSAGES/django.mo +0 -0
- wagtail/images/locale/sk_SK/LC_MESSAGES/django.po +2 -2
- wagtail/images/locale/sl/LC_MESSAGES/django.mo +0 -0
- wagtail/images/locale/sl/LC_MESSAGES/django.po +2 -2
- wagtail/images/locale/sv/LC_MESSAGES/django.mo +0 -0
- wagtail/images/locale/sv/LC_MESSAGES/django.po +2 -2
- wagtail/images/locale/tet/LC_MESSAGES/django.po +1 -1
- wagtail/images/locale/th/LC_MESSAGES/django.po +1 -1
- wagtail/images/locale/tr/LC_MESSAGES/django.po +1 -1
- wagtail/images/locale/tr_TR/LC_MESSAGES/django.po +1 -1
- wagtail/images/locale/ug/LC_MESSAGES/django.po +1 -1
- wagtail/images/locale/uk/LC_MESSAGES/django.mo +0 -0
- wagtail/images/locale/uk/LC_MESSAGES/django.po +5 -4
- wagtail/images/locale/vi/LC_MESSAGES/django.po +1 -1
- wagtail/images/locale/zh/LC_MESSAGES/django.po +1 -1
- wagtail/images/locale/zh_Hans/LC_MESSAGES/django.po +2 -1
- wagtail/images/locale/zh_Hant/LC_MESSAGES/django.po +1 -1
- wagtail/images/management/commands/wagtail_update_image_renditions.py +1 -1
- wagtail/images/models.py +23 -7
- wagtail/images/tests/test_admin_views.py +1 -1
- wagtail/images/tests/test_models.py +39 -3
- wagtail/images/tests/tests.py +0 -6
- wagtail/images/wagtail_hooks.py +2 -2
- wagtail/locale/af/LC_MESSAGES/django.po +1 -1
- wagtail/locale/ar/LC_MESSAGES/django.po +50 -50
- wagtail/locale/az_AZ/LC_MESSAGES/django.po +1 -1
- wagtail/locale/be/LC_MESSAGES/django.mo +0 -0
- wagtail/locale/be/LC_MESSAGES/django.po +52 -51
- wagtail/locale/bg/LC_MESSAGES/django.po +4 -4
- wagtail/locale/bn/LC_MESSAGES/django.po +1 -1
- wagtail/locale/ca/LC_MESSAGES/django.mo +0 -0
- wagtail/locale/ca/LC_MESSAGES/django.po +234 -234
- wagtail/locale/cs/LC_MESSAGES/django.mo +0 -0
- wagtail/locale/cs/LC_MESSAGES/django.po +172 -172
- wagtail/locale/cy/LC_MESSAGES/django.po +12 -12
- wagtail/locale/da/LC_MESSAGES/django.po +7 -7
- wagtail/locale/de/LC_MESSAGES/django.mo +0 -0
- wagtail/locale/de/LC_MESSAGES/django.po +237 -236
- wagtail/locale/dv/LC_MESSAGES/django.po +216 -215
- wagtail/locale/el/LC_MESSAGES/django.po +76 -75
- wagtail/locale/en/LC_MESSAGES/django.po +339 -339
- wagtail/locale/es/LC_MESSAGES/django.mo +0 -0
- wagtail/locale/es/LC_MESSAGES/django.po +192 -192
- wagtail/locale/es_419/LC_MESSAGES/django.po +1 -1
- wagtail/locale/et/LC_MESSAGES/django.mo +0 -0
- wagtail/locale/et/LC_MESSAGES/django.po +173 -173
- wagtail/locale/eu/LC_MESSAGES/django.po +1 -1
- wagtail/locale/fa/LC_MESSAGES/django.po +50 -50
- wagtail/locale/fi/LC_MESSAGES/django.mo +0 -0
- wagtail/locale/fi/LC_MESSAGES/django.po +187 -187
- wagtail/locale/fr/LC_MESSAGES/django.mo +0 -0
- wagtail/locale/fr/LC_MESSAGES/django.po +236 -235
- wagtail/locale/gl/LC_MESSAGES/django.mo +0 -0
- wagtail/locale/gl/LC_MESSAGES/django.po +235 -235
- wagtail/locale/he_IL/LC_MESSAGES/django.po +9 -9
- wagtail/locale/hr_HR/LC_MESSAGES/django.po +184 -184
- wagtail/locale/hu/LC_MESSAGES/django.mo +0 -0
- wagtail/locale/hu/LC_MESSAGES/django.po +251 -229
- wagtail/locale/id_ID/LC_MESSAGES/django.po +49 -49
- wagtail/locale/is_IS/LC_MESSAGES/django.po +232 -232
- wagtail/locale/it/LC_MESSAGES/django.mo +0 -0
- wagtail/locale/it/LC_MESSAGES/django.po +224 -224
- wagtail/locale/ja/LC_MESSAGES/django.po +110 -110
- wagtail/locale/ka/LC_MESSAGES/django.po +4 -4
- wagtail/locale/ko/LC_MESSAGES/django.mo +0 -0
- wagtail/locale/ko/LC_MESSAGES/django.po +88 -53
- wagtail/locale/lt/LC_MESSAGES/django.po +53 -53
- wagtail/locale/lv/LC_MESSAGES/django.po +22 -22
- wagtail/locale/mi/LC_MESSAGES/django.po +167 -167
- wagtail/locale/mn/LC_MESSAGES/django.mo +0 -0
- wagtail/locale/mn/LC_MESSAGES/django.po +51 -50
- wagtail/locale/my/LC_MESSAGES/django.po +6 -6
- wagtail/locale/nb/LC_MESSAGES/django.po +50 -50
- wagtail/locale/nl/LC_MESSAGES/django.mo +0 -0
- wagtail/locale/nl/LC_MESSAGES/django.po +233 -233
- wagtail/locale/pl/LC_MESSAGES/django.mo +0 -0
- wagtail/locale/pl/LC_MESSAGES/django.po +232 -232
- wagtail/locale/pt_BR/LC_MESSAGES/django.mo +0 -0
- wagtail/locale/pt_BR/LC_MESSAGES/django.po +202 -202
- wagtail/locale/pt_PT/LC_MESSAGES/django.mo +0 -0
- wagtail/locale/pt_PT/LC_MESSAGES/django.po +180 -178
- wagtail/locale/ro/LC_MESSAGES/django.mo +0 -0
- wagtail/locale/ro/LC_MESSAGES/django.po +233 -232
- wagtail/locale/ru/LC_MESSAGES/django.mo +0 -0
- wagtail/locale/ru/LC_MESSAGES/django.po +237 -234
- wagtail/locale/sk_SK/LC_MESSAGES/django.po +47 -47
- wagtail/locale/sl/LC_MESSAGES/django.mo +0 -0
- wagtail/locale/sl/LC_MESSAGES/django.po +233 -233
- wagtail/locale/sv/LC_MESSAGES/django.mo +0 -0
- wagtail/locale/sv/LC_MESSAGES/django.po +207 -207
- wagtail/locale/tet/LC_MESSAGES/django.po +37 -37
- wagtail/locale/th/LC_MESSAGES/django.mo +0 -0
- wagtail/locale/th/LC_MESSAGES/django.po +54 -53
- wagtail/locale/tr/LC_MESSAGES/django.po +50 -50
- wagtail/locale/tr_TR/LC_MESSAGES/django.po +50 -50
- wagtail/locale/ug/LC_MESSAGES/django.po +232 -232
- wagtail/locale/uk/LC_MESSAGES/django.mo +0 -0
- wagtail/locale/uk/LC_MESSAGES/django.po +185 -185
- wagtail/locale/vi/LC_MESSAGES/django.mo +0 -0
- wagtail/locale/vi/LC_MESSAGES/django.po +20 -19
- wagtail/locale/zh/LC_MESSAGES/django.po +4 -4
- wagtail/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
- wagtail/locale/zh_Hans/LC_MESSAGES/django.po +189 -189
- wagtail/locale/zh_Hant/LC_MESSAGES/django.po +161 -161
- wagtail/locales/locale/ar/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/be/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/bg/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/ca/LC_MESSAGES/django.mo +0 -0
- wagtail/locales/locale/ca/LC_MESSAGES/django.po +13 -3
- wagtail/locales/locale/cs/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/cy/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/da/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/de/LC_MESSAGES/django.mo +0 -0
- wagtail/locales/locale/de/LC_MESSAGES/django.po +13 -2
- wagtail/locales/locale/dv/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/el/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/en/LC_MESSAGES/django.po +26 -14
- wagtail/locales/locale/es/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/et/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/fa/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/fi/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/fr/LC_MESSAGES/django.mo +0 -0
- wagtail/locales/locale/fr/LC_MESSAGES/django.po +14 -2
- wagtail/locales/locale/gl/LC_MESSAGES/django.mo +0 -0
- wagtail/locales/locale/gl/LC_MESSAGES/django.po +13 -3
- wagtail/locales/locale/he_IL/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/hr_HR/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/hu/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/id_ID/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/is_IS/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/it/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/ja/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/ko/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/lt/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/lv/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/mi/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/mn/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/my/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/nb/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/nl/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/pl/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/pt_BR/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/pt_PT/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/ro/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/ru/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/sk_SK/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/sl/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/sv/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/tet/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/th/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/tr/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/tr_TR/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/ug/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/uk/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/vi/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/zh/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/zh_Hans/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/zh_Hant/LC_MESSAGES/django.po +1 -1
- wagtail/locales/tests.py +32 -1
- wagtail/locales/views.py +39 -7
- wagtail/models/__init__.py +67 -5002
- wagtail/models/audit_log.py +1 -1
- wagtail/models/content_types.py +11 -0
- wagtail/models/draft_state.py +197 -0
- wagtail/models/locking.py +82 -0
- wagtail/models/orderable.py +10 -0
- wagtail/models/pages.py +2755 -0
- wagtail/models/preview.py +281 -0
- wagtail/models/reference_index.py +1 -1
- wagtail/models/revisions.py +433 -0
- wagtail/models/view_restrictions.py +3 -3
- wagtail/models/workflows.py +1348 -0
- wagtail/project_template/home/apps.py +6 -0
- wagtail/project_template/manage.py-tpl +22 -0
- wagtail/project_template/project_name/settings/base.py +1 -0
- wagtail/project_template/requirements.txt +2 -2
- wagtail/search/backends/base.py +4 -0
- wagtail/search/backends/database/mysql/query.py +8 -4
- wagtail/search/backends/elasticsearch7.py +17 -7
- wagtail/search/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/search/tests/elasticsearch_common_tests.py +43 -0
- wagtail/search/tests/test_backends.py +43 -13
- wagtail/search/tests/test_mysql_backend.py +22 -0
- wagtail/search/tests/test_page_search.py +2 -1
- wagtail/search/tests/test_queries.py +2 -2
- wagtail/sites/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/snippets/action_menu.py +9 -30
- wagtail/snippets/locale/af/LC_MESSAGES/django.po +1 -1
- wagtail/snippets/locale/ar/LC_MESSAGES/django.po +4 -4
- wagtail/snippets/locale/az_AZ/LC_MESSAGES/django.po +4 -4
- wagtail/snippets/locale/be/LC_MESSAGES/django.mo +0 -0
- wagtail/snippets/locale/be/LC_MESSAGES/django.po +6 -5
- wagtail/snippets/locale/bg/LC_MESSAGES/django.po +4 -4
- wagtail/snippets/locale/bn/LC_MESSAGES/django.po +4 -4
- wagtail/snippets/locale/ca/LC_MESSAGES/django.mo +0 -0
- wagtail/snippets/locale/ca/LC_MESSAGES/django.po +11 -5
- wagtail/snippets/locale/cs/LC_MESSAGES/django.po +5 -4
- wagtail/snippets/locale/cy/LC_MESSAGES/django.po +5 -4
- wagtail/snippets/locale/da/LC_MESSAGES/django.po +4 -4
- wagtail/snippets/locale/de/LC_MESSAGES/django.mo +0 -0
- wagtail/snippets/locale/de/LC_MESSAGES/django.po +13 -6
- wagtail/snippets/locale/dv/LC_MESSAGES/django.po +5 -4
- wagtail/snippets/locale/el/LC_MESSAGES/django.po +5 -4
- wagtail/snippets/locale/en/LC_MESSAGES/django.po +31 -23
- wagtail/snippets/locale/en_IN/LC_MESSAGES/django.po +1 -1
- wagtail/snippets/locale/es/LC_MESSAGES/django.mo +0 -0
- wagtail/snippets/locale/es/LC_MESSAGES/django.po +6 -5
- wagtail/snippets/locale/es_419/LC_MESSAGES/django.po +4 -4
- wagtail/snippets/locale/es_VE/LC_MESSAGES/django.po +1 -1
- wagtail/snippets/locale/et/LC_MESSAGES/django.po +4 -4
- wagtail/snippets/locale/eu/LC_MESSAGES/django.po +1 -1
- wagtail/snippets/locale/fa/LC_MESSAGES/django.mo +0 -0
- wagtail/snippets/locale/fa/LC_MESSAGES/django.po +6 -5
- wagtail/snippets/locale/fi/LC_MESSAGES/django.mo +0 -0
- wagtail/snippets/locale/fi/LC_MESSAGES/django.po +6 -5
- wagtail/snippets/locale/fr/LC_MESSAGES/django.mo +0 -0
- wagtail/snippets/locale/fr/LC_MESSAGES/django.po +13 -6
- wagtail/snippets/locale/gl/LC_MESSAGES/django.mo +0 -0
- wagtail/snippets/locale/gl/LC_MESSAGES/django.po +11 -5
- wagtail/snippets/locale/he_IL/LC_MESSAGES/django.po +4 -4
- wagtail/snippets/locale/hi/LC_MESSAGES/django.po +1 -1
- wagtail/snippets/locale/hr_HR/LC_MESSAGES/django.po +4 -4
- wagtail/snippets/locale/ht/LC_MESSAGES/django.po +1 -1
- wagtail/snippets/locale/hu/LC_MESSAGES/django.po +4 -4
- wagtail/snippets/locale/id_ID/LC_MESSAGES/django.po +4 -4
- wagtail/snippets/locale/is_IS/LC_MESSAGES/django.mo +0 -0
- wagtail/snippets/locale/is_IS/LC_MESSAGES/django.po +5 -6
- wagtail/snippets/locale/it/LC_MESSAGES/django.mo +0 -0
- wagtail/snippets/locale/it/LC_MESSAGES/django.po +5 -5
- wagtail/snippets/locale/ja/LC_MESSAGES/django.mo +0 -0
- wagtail/snippets/locale/ja/LC_MESSAGES/django.po +6 -5
- wagtail/snippets/locale/ka/LC_MESSAGES/django.po +4 -4
- wagtail/snippets/locale/ko/LC_MESSAGES/django.po +4 -4
- wagtail/snippets/locale/lt/LC_MESSAGES/django.po +4 -4
- wagtail/snippets/locale/lv/LC_MESSAGES/django.po +4 -4
- wagtail/snippets/locale/mi/LC_MESSAGES/django.po +4 -4
- wagtail/snippets/locale/mn/LC_MESSAGES/django.po +4 -4
- wagtail/snippets/locale/my/LC_MESSAGES/django.po +4 -4
- wagtail/snippets/locale/nb/LC_MESSAGES/django.po +5 -4
- wagtail/snippets/locale/nl/LC_MESSAGES/django.mo +0 -0
- wagtail/snippets/locale/nl/LC_MESSAGES/django.po +12 -5
- wagtail/snippets/locale/pl/LC_MESSAGES/django.mo +0 -0
- wagtail/snippets/locale/pl/LC_MESSAGES/django.po +5 -5
- wagtail/snippets/locale/pt_BR/LC_MESSAGES/django.mo +0 -0
- wagtail/snippets/locale/pt_BR/LC_MESSAGES/django.po +7 -5
- wagtail/snippets/locale/pt_PT/LC_MESSAGES/django.mo +0 -0
- wagtail/snippets/locale/pt_PT/LC_MESSAGES/django.po +7 -5
- wagtail/snippets/locale/ro/LC_MESSAGES/django.mo +0 -0
- wagtail/snippets/locale/ro/LC_MESSAGES/django.po +6 -5
- wagtail/snippets/locale/ru/LC_MESSAGES/django.mo +0 -0
- wagtail/snippets/locale/ru/LC_MESSAGES/django.po +6 -5
- wagtail/snippets/locale/sk_SK/LC_MESSAGES/django.po +4 -4
- wagtail/snippets/locale/sl/LC_MESSAGES/django.mo +0 -0
- wagtail/snippets/locale/sl/LC_MESSAGES/django.po +5 -5
- wagtail/snippets/locale/sr/LC_MESSAGES/django.po +1 -1
- wagtail/snippets/locale/sv/LC_MESSAGES/django.mo +0 -0
- wagtail/snippets/locale/sv/LC_MESSAGES/django.po +5 -5
- wagtail/snippets/locale/ta/LC_MESSAGES/django.po +1 -1
- wagtail/snippets/locale/tet/LC_MESSAGES/django.po +4 -4
- wagtail/snippets/locale/th/LC_MESSAGES/django.mo +0 -0
- wagtail/snippets/locale/th/LC_MESSAGES/django.po +6 -5
- wagtail/snippets/locale/tr/LC_MESSAGES/django.po +4 -4
- wagtail/snippets/locale/tr_TR/LC_MESSAGES/django.po +4 -4
- wagtail/snippets/locale/ug/LC_MESSAGES/django.po +4 -4
- wagtail/snippets/locale/uk/LC_MESSAGES/django.mo +0 -0
- wagtail/snippets/locale/uk/LC_MESSAGES/django.po +6 -6
- wagtail/snippets/locale/vi/LC_MESSAGES/django.po +4 -4
- wagtail/snippets/locale/zh/LC_MESSAGES/django.po +4 -4
- wagtail/snippets/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
- wagtail/snippets/locale/zh_Hans/LC_MESSAGES/django.po +6 -5
- wagtail/snippets/locale/zh_Hant/LC_MESSAGES/django.po +4 -4
- wagtail/snippets/permissions.py +6 -4
- wagtail/snippets/static/wagtailsnippets/js/snippet-chooser-telepath.js +1 -1
- wagtail/snippets/static/wagtailsnippets/js/snippet-chooser.js +1 -1
- wagtail/snippets/templates/wagtailsnippets/snippets/action_menu/publish.html +3 -5
- wagtail/snippets/tests/test_preview.py +62 -38
- wagtail/snippets/tests/test_snippets.py +313 -130
- wagtail/snippets/tests/test_viewset.py +69 -6
- wagtail/snippets/tests/test_workflows.py +0 -4
- wagtail/snippets/views/snippets.py +29 -53
- wagtail/snippets/wagtail_hooks.py +3 -7
- wagtail/test/apps.py +5 -0
- wagtail/test/context_processors.py +19 -0
- wagtail/test/customuser/forms.py +19 -0
- wagtail/test/customuser/viewsets.py +12 -0
- wagtail/test/settings.py +8 -9
- wagtail/test/testapp/migrations/0046_alter_custom_rendition_to_unique_constraint.py +24 -0
- wagtail/test/testapp/migrations/{0046_advertwithcustomuuidprimarykey_page.py → 0047_advertwithcustomuuidprimarykey_page.py} +1 -1
- wagtail/test/testapp/migrations/0048_requireddatepage.py +36 -0
- wagtail/test/testapp/migrations/0049_promotionalpage.py +35 -0
- wagtail/test/testapp/migrations/0050_headcountrelatedmodelusingpk_related_page.py +25 -0
- wagtail/test/testapp/migrations/0051_userapprovaltaskstate_userapprovaltask.py +59 -0
- wagtail/test/testapp/models.py +183 -26
- wagtail/test/testapp/templates/tests/workflows/approve_with_style.html +6 -0
- wagtail/test/testapp/views.py +7 -1
- wagtail/test/testapp/wagtail_hooks.py +6 -0
- wagtail/test/utils/form_data.py +1 -0
- wagtail/tests/test_blocks.py +0 -18
- wagtail/tests/test_management_commands.py +1 -1
- wagtail/tests/test_page_model.py +27 -0
- wagtail/tests/test_page_privacy.py +1 -1
- wagtail/tests/test_reference_index.py +26 -0
- wagtail/tests/test_streamfield.py +136 -0
- wagtail/tests/test_utils.py +1 -23
- wagtail/users/forms.py +3 -17
- wagtail/users/locale/ar/LC_MESSAGES/django.po +1 -1
- wagtail/users/locale/be/LC_MESSAGES/django.mo +0 -0
- wagtail/users/locale/be/LC_MESSAGES/django.po +3 -2
- wagtail/users/locale/bg/LC_MESSAGES/django.po +1 -1
- wagtail/users/locale/bn/LC_MESSAGES/django.po +1 -1
- wagtail/users/locale/ca/LC_MESSAGES/django.mo +0 -0
- wagtail/users/locale/ca/LC_MESSAGES/django.po +2 -2
- wagtail/users/locale/cs/LC_MESSAGES/django.po +2 -1
- wagtail/users/locale/cy/LC_MESSAGES/django.po +2 -1
- wagtail/users/locale/da/LC_MESSAGES/django.po +1 -1
- wagtail/users/locale/de/LC_MESSAGES/django.mo +0 -0
- wagtail/users/locale/de/LC_MESSAGES/django.po +3 -3
- wagtail/users/locale/dv/LC_MESSAGES/django.po +2 -1
- wagtail/users/locale/el/LC_MESSAGES/django.po +1 -1
- wagtail/users/locale/en/LC_MESSAGES/django.po +71 -71
- wagtail/users/locale/es/LC_MESSAGES/django.po +1 -1
- wagtail/users/locale/es_419/LC_MESSAGES/django.po +1 -1
- wagtail/users/locale/et/LC_MESSAGES/django.po +1 -1
- wagtail/users/locale/eu/LC_MESSAGES/django.po +1 -1
- wagtail/users/locale/fa/LC_MESSAGES/django.mo +0 -0
- wagtail/users/locale/fa/LC_MESSAGES/django.po +3 -2
- wagtail/users/locale/fi/LC_MESSAGES/django.mo +0 -0
- wagtail/users/locale/fi/LC_MESSAGES/django.po +2 -2
- wagtail/users/locale/fr/LC_MESSAGES/django.mo +0 -0
- wagtail/users/locale/fr/LC_MESSAGES/django.po +4 -2
- wagtail/users/locale/gl/LC_MESSAGES/django.mo +0 -0
- wagtail/users/locale/gl/LC_MESSAGES/django.po +2 -2
- wagtail/users/locale/he_IL/LC_MESSAGES/django.po +1 -1
- wagtail/users/locale/hr_HR/LC_MESSAGES/django.mo +0 -0
- wagtail/users/locale/hr_HR/LC_MESSAGES/django.po +2 -2
- wagtail/users/locale/hu/LC_MESSAGES/django.mo +0 -0
- wagtail/users/locale/hu/LC_MESSAGES/django.po +45 -3
- wagtail/users/locale/hy/LC_MESSAGES/django.po +1 -1
- wagtail/users/locale/id_ID/LC_MESSAGES/django.po +1 -1
- wagtail/users/locale/is_IS/LC_MESSAGES/django.po +1 -1
- wagtail/users/locale/it/LC_MESSAGES/django.mo +0 -0
- wagtail/users/locale/it/LC_MESSAGES/django.po +2 -2
- wagtail/users/locale/ja/LC_MESSAGES/django.po +1 -1
- wagtail/users/locale/ka/LC_MESSAGES/django.po +1 -1
- wagtail/users/locale/ko/LC_MESSAGES/django.mo +0 -0
- wagtail/users/locale/ko/LC_MESSAGES/django.po +47 -2
- wagtail/users/locale/lt/LC_MESSAGES/django.po +1 -1
- wagtail/users/locale/lv/LC_MESSAGES/django.po +1 -1
- wagtail/users/locale/mi/LC_MESSAGES/django.po +1 -1
- wagtail/users/locale/mn/LC_MESSAGES/django.mo +0 -0
- wagtail/users/locale/mn/LC_MESSAGES/django.po +3 -2
- wagtail/users/locale/my/LC_MESSAGES/django.po +1 -1
- wagtail/users/locale/nb/LC_MESSAGES/django.po +2 -1
- wagtail/users/locale/nl/LC_MESSAGES/django.mo +0 -0
- wagtail/users/locale/nl/LC_MESSAGES/django.po +3 -2
- wagtail/users/locale/pl/LC_MESSAGES/django.mo +0 -0
- wagtail/users/locale/pl/LC_MESSAGES/django.po +2 -2
- wagtail/users/locale/pt_BR/LC_MESSAGES/django.mo +0 -0
- wagtail/users/locale/pt_BR/LC_MESSAGES/django.po +3 -2
- wagtail/users/locale/pt_PT/LC_MESSAGES/django.mo +0 -0
- wagtail/users/locale/pt_PT/LC_MESSAGES/django.po +3 -2
- wagtail/users/locale/ro/LC_MESSAGES/django.mo +0 -0
- wagtail/users/locale/ro/LC_MESSAGES/django.po +3 -2
- wagtail/users/locale/ru/LC_MESSAGES/django.mo +0 -0
- wagtail/users/locale/ru/LC_MESSAGES/django.po +4 -2
- wagtail/users/locale/sk_SK/LC_MESSAGES/django.mo +0 -0
- wagtail/users/locale/sk_SK/LC_MESSAGES/django.po +2 -2
- wagtail/users/locale/sl/LC_MESSAGES/django.mo +0 -0
- wagtail/users/locale/sl/LC_MESSAGES/django.po +2 -2
- wagtail/users/locale/sv/LC_MESSAGES/django.mo +0 -0
- wagtail/users/locale/sv/LC_MESSAGES/django.po +2 -2
- wagtail/users/locale/tet/LC_MESSAGES/django.po +1 -1
- wagtail/users/locale/th/LC_MESSAGES/django.mo +0 -0
- wagtail/users/locale/th/LC_MESSAGES/django.po +3 -2
- wagtail/users/locale/tr/LC_MESSAGES/django.po +1 -1
- wagtail/users/locale/tr_TR/LC_MESSAGES/django.po +1 -1
- wagtail/users/locale/ug/LC_MESSAGES/django.po +1 -1
- wagtail/users/locale/uk/LC_MESSAGES/django.mo +0 -0
- wagtail/users/locale/uk/LC_MESSAGES/django.po +3 -2
- wagtail/users/locale/vi/LC_MESSAGES/django.po +2 -1
- wagtail/users/locale/zh/LC_MESSAGES/django.po +1 -1
- wagtail/users/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
- wagtail/users/locale/zh_Hans/LC_MESSAGES/django.po +3 -2
- wagtail/users/locale/zh_Hant/LC_MESSAGES/django.po +1 -1
- wagtail/users/models.py +7 -1
- wagtail/users/templatetags/wagtailusers_tags.py +0 -29
- wagtail/users/tests/__init__.py +0 -6
- wagtail/users/tests/test_admin_views.py +285 -611
- wagtail/users/views/groups.py +1 -28
- wagtail/users/views/users.py +3 -45
- wagtail/utils/deprecation.py +3 -3
- wagtail/whitelist.py +1 -0
- wagtail/workflows.py +2 -2
- wagtail-7.0.dist-info/METADATA +224 -0
- {wagtail-6.4.1.dist-info → wagtail-7.0.dist-info}/RECORD +1230 -1210
- {wagtail-6.4.1.dist-info → wagtail-7.0.dist-info}/WHEEL +1 -1
- wagtail/admin/templates/wagtailadmin/shared/ajax_pagination_nav.html +0 -25
- wagtail/project_template/manage.py +0 -10
- wagtail/utils/setup.py +0 -90
- wagtail/utils/widgets.py +0 -46
- wagtail-6.4.1.dist-info/METADATA +0 -77
- {wagtail-6.4.1.dist-info → wagtail-7.0.dist-info}/entry_points.txt +0 -0
- {wagtail-6.4.1.dist-info → wagtail-7.0.dist-info/licenses}/LICENSE +0 -0
- {wagtail-6.4.1.dist-info → wagtail-7.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,1348 @@
|
|
|
1
|
+
from django import forms
|
|
2
|
+
from django.conf import settings
|
|
3
|
+
from django.contrib.auth.models import Group
|
|
4
|
+
from django.contrib.contenttypes.fields import GenericForeignKey
|
|
5
|
+
from django.contrib.contenttypes.models import ContentType
|
|
6
|
+
from django.core import checks
|
|
7
|
+
from django.core.exceptions import PermissionDenied, ValidationError
|
|
8
|
+
from django.db import models, transaction
|
|
9
|
+
from django.db.models import Q
|
|
10
|
+
from django.db.models.expressions import OuterRef, Subquery
|
|
11
|
+
from django.utils import timezone
|
|
12
|
+
from django.utils.functional import cached_property
|
|
13
|
+
from django.utils.module_loading import import_string
|
|
14
|
+
from django.utils.text import capfirst
|
|
15
|
+
from django.utils.translation import gettext_lazy as _
|
|
16
|
+
from modelcluster.fields import ParentalKey
|
|
17
|
+
from modelcluster.models import (
|
|
18
|
+
ClusterableModel,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
from wagtail.coreutils import get_content_type_label
|
|
22
|
+
from wagtail.forms import TaskStateCommentForm
|
|
23
|
+
from wagtail.locks import WorkflowLock
|
|
24
|
+
from wagtail.log_actions import log
|
|
25
|
+
from wagtail.query import SpecificQuerySetMixin
|
|
26
|
+
from wagtail.signals import (
|
|
27
|
+
task_approved,
|
|
28
|
+
task_cancelled,
|
|
29
|
+
task_rejected,
|
|
30
|
+
task_submitted,
|
|
31
|
+
workflow_approved,
|
|
32
|
+
workflow_cancelled,
|
|
33
|
+
workflow_rejected,
|
|
34
|
+
workflow_submitted,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
from .copying import _copy, _copy_m2m_relations
|
|
38
|
+
from .draft_state import DraftStateMixin
|
|
39
|
+
from .locking import LockableMixin
|
|
40
|
+
from .orderable import Orderable
|
|
41
|
+
from .revisions import Revision, RevisionMixin
|
|
42
|
+
from .specific import SpecificMixin
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class WorkflowContentType(models.Model):
|
|
46
|
+
content_type = models.OneToOneField(
|
|
47
|
+
ContentType,
|
|
48
|
+
related_name="wagtail_workflow_content_type",
|
|
49
|
+
verbose_name=_("content type"),
|
|
50
|
+
on_delete=models.CASCADE,
|
|
51
|
+
primary_key=True,
|
|
52
|
+
unique=True,
|
|
53
|
+
)
|
|
54
|
+
workflow = models.ForeignKey(
|
|
55
|
+
"Workflow",
|
|
56
|
+
related_name="workflow_content_types",
|
|
57
|
+
verbose_name=_("workflow"),
|
|
58
|
+
on_delete=models.CASCADE,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
def __str__(self):
|
|
62
|
+
content_type_label = get_content_type_label(self.content_type)
|
|
63
|
+
return f"WorkflowContentType: {content_type_label} - {self.workflow}"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class WorkflowStateQuerySet(models.QuerySet):
|
|
67
|
+
def active(self):
|
|
68
|
+
"""
|
|
69
|
+
Filters to only ``STATUS_IN_PROGRESS`` and ``STATUS_NEEDS_CHANGES`` WorkflowStates.
|
|
70
|
+
"""
|
|
71
|
+
return self.filter(
|
|
72
|
+
Q(status=WorkflowState.STATUS_IN_PROGRESS)
|
|
73
|
+
| Q(status=WorkflowState.STATUS_NEEDS_CHANGES)
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
def for_instance(self, instance):
|
|
77
|
+
"""
|
|
78
|
+
Filters to only WorkflowStates for the given instance.
|
|
79
|
+
"""
|
|
80
|
+
try:
|
|
81
|
+
# Use RevisionMixin.get_base_content_type() if available
|
|
82
|
+
return self.filter(
|
|
83
|
+
base_content_type=instance.get_base_content_type(),
|
|
84
|
+
object_id=str(instance.pk),
|
|
85
|
+
)
|
|
86
|
+
except AttributeError:
|
|
87
|
+
# Fallback to ContentType for the model
|
|
88
|
+
return self.filter(
|
|
89
|
+
content_type=ContentType.objects.get_for_model(
|
|
90
|
+
instance, for_concrete_model=False
|
|
91
|
+
),
|
|
92
|
+
object_id=str(instance.pk),
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
WorkflowStateManager = models.Manager.from_queryset(WorkflowStateQuerySet)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class WorkflowState(models.Model):
|
|
100
|
+
"""Tracks the status of a started Workflow on an object."""
|
|
101
|
+
|
|
102
|
+
STATUS_IN_PROGRESS = "in_progress"
|
|
103
|
+
STATUS_APPROVED = "approved"
|
|
104
|
+
STATUS_NEEDS_CHANGES = "needs_changes"
|
|
105
|
+
STATUS_CANCELLED = "cancelled"
|
|
106
|
+
STATUS_CHOICES = (
|
|
107
|
+
(STATUS_IN_PROGRESS, _("In progress")),
|
|
108
|
+
(STATUS_APPROVED, _("Approved")),
|
|
109
|
+
(STATUS_NEEDS_CHANGES, _("Needs changes")),
|
|
110
|
+
(STATUS_CANCELLED, _("Cancelled")),
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
content_type = models.ForeignKey(
|
|
114
|
+
ContentType, on_delete=models.CASCADE, related_name="+"
|
|
115
|
+
)
|
|
116
|
+
base_content_type = models.ForeignKey(
|
|
117
|
+
ContentType, on_delete=models.CASCADE, related_name="+"
|
|
118
|
+
)
|
|
119
|
+
object_id = models.CharField(max_length=255, verbose_name=_("object id"))
|
|
120
|
+
|
|
121
|
+
content_object = GenericForeignKey(
|
|
122
|
+
"base_content_type", "object_id", for_concrete_model=False
|
|
123
|
+
)
|
|
124
|
+
content_object.wagtail_reference_index_ignore = True
|
|
125
|
+
|
|
126
|
+
workflow = models.ForeignKey(
|
|
127
|
+
"Workflow",
|
|
128
|
+
on_delete=models.CASCADE,
|
|
129
|
+
verbose_name=_("workflow"),
|
|
130
|
+
related_name="workflow_states",
|
|
131
|
+
)
|
|
132
|
+
status = models.fields.CharField(
|
|
133
|
+
choices=STATUS_CHOICES,
|
|
134
|
+
verbose_name=_("status"),
|
|
135
|
+
max_length=50,
|
|
136
|
+
default=STATUS_IN_PROGRESS,
|
|
137
|
+
)
|
|
138
|
+
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("created at"))
|
|
139
|
+
requested_by = models.ForeignKey(
|
|
140
|
+
settings.AUTH_USER_MODEL,
|
|
141
|
+
verbose_name=_("requested by"),
|
|
142
|
+
null=True,
|
|
143
|
+
blank=True,
|
|
144
|
+
editable=True,
|
|
145
|
+
on_delete=models.SET_NULL,
|
|
146
|
+
related_name="requested_workflows",
|
|
147
|
+
)
|
|
148
|
+
current_task_state = models.OneToOneField(
|
|
149
|
+
"TaskState",
|
|
150
|
+
on_delete=models.SET_NULL,
|
|
151
|
+
null=True,
|
|
152
|
+
blank=True,
|
|
153
|
+
verbose_name=_("current task state"),
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
# allows a custom function to be called on finishing the Workflow successfully.
|
|
157
|
+
on_finish = import_string(
|
|
158
|
+
getattr(
|
|
159
|
+
settings,
|
|
160
|
+
"WAGTAIL_FINISH_WORKFLOW_ACTION",
|
|
161
|
+
"wagtail.workflows.publish_workflow_state",
|
|
162
|
+
)
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
objects = WorkflowStateManager()
|
|
166
|
+
|
|
167
|
+
def clean(self):
|
|
168
|
+
super().clean()
|
|
169
|
+
|
|
170
|
+
if self.status in (self.STATUS_IN_PROGRESS, self.STATUS_NEEDS_CHANGES):
|
|
171
|
+
# The unique constraint is conditional, and so not supported on the MySQL backend - so an additional check is done here
|
|
172
|
+
if (
|
|
173
|
+
WorkflowState.objects.active()
|
|
174
|
+
.filter(
|
|
175
|
+
base_content_type_id=self.base_content_type_id,
|
|
176
|
+
object_id=self.object_id,
|
|
177
|
+
)
|
|
178
|
+
.exclude(pk=self.pk)
|
|
179
|
+
.exists()
|
|
180
|
+
):
|
|
181
|
+
raise ValidationError(
|
|
182
|
+
_(
|
|
183
|
+
"There may only be one in progress or needs changes workflow state per page/snippet."
|
|
184
|
+
)
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
def save(self, *args, **kwargs):
|
|
188
|
+
self.full_clean()
|
|
189
|
+
return super().save(*args, **kwargs)
|
|
190
|
+
|
|
191
|
+
def __str__(self):
|
|
192
|
+
return _(
|
|
193
|
+
"Workflow '%(workflow_name)s' on %(model_name)s '%(title)s': %(status)s"
|
|
194
|
+
) % {
|
|
195
|
+
"workflow_name": self.workflow,
|
|
196
|
+
"model_name": self.content_object._meta.verbose_name,
|
|
197
|
+
"title": self.content_object,
|
|
198
|
+
"status": self.status,
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
def resume(self, user=None):
|
|
202
|
+
"""Put a STATUS_NEEDS_CHANGES workflow state back into STATUS_IN_PROGRESS, and restart the current task"""
|
|
203
|
+
from wagtail.models import Page
|
|
204
|
+
|
|
205
|
+
if self.status != self.STATUS_NEEDS_CHANGES:
|
|
206
|
+
raise PermissionDenied
|
|
207
|
+
revision = self.current_task_state.revision
|
|
208
|
+
current_task_state = self.current_task_state
|
|
209
|
+
self.current_task_state = None
|
|
210
|
+
self.status = self.STATUS_IN_PROGRESS
|
|
211
|
+
self.save()
|
|
212
|
+
|
|
213
|
+
instance = self.content_object
|
|
214
|
+
if isinstance(instance, Page):
|
|
215
|
+
instance = self.content_object.specific
|
|
216
|
+
|
|
217
|
+
log(
|
|
218
|
+
instance=instance,
|
|
219
|
+
action="wagtail.workflow.resume",
|
|
220
|
+
data={
|
|
221
|
+
"workflow": {
|
|
222
|
+
"id": self.workflow_id,
|
|
223
|
+
"title": self.workflow.name,
|
|
224
|
+
"status": self.status,
|
|
225
|
+
"task_state_id": current_task_state.id,
|
|
226
|
+
"task": {
|
|
227
|
+
"id": current_task_state.task.id,
|
|
228
|
+
"title": current_task_state.task.name,
|
|
229
|
+
},
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
revision=revision,
|
|
233
|
+
user=user,
|
|
234
|
+
)
|
|
235
|
+
return self.update(user=user, next_task=current_task_state.task)
|
|
236
|
+
|
|
237
|
+
def user_can_cancel(self, user):
|
|
238
|
+
if (
|
|
239
|
+
isinstance(self.content_object, LockableMixin)
|
|
240
|
+
and self.content_object.locked
|
|
241
|
+
and self.content_object.locked_by != user
|
|
242
|
+
):
|
|
243
|
+
return False
|
|
244
|
+
return (
|
|
245
|
+
user == self.requested_by
|
|
246
|
+
or user == getattr(self.content_object, "owner", None)
|
|
247
|
+
or (
|
|
248
|
+
self.current_task_state
|
|
249
|
+
and self.current_task_state.status
|
|
250
|
+
== self.current_task_state.STATUS_IN_PROGRESS
|
|
251
|
+
and "approve"
|
|
252
|
+
in [
|
|
253
|
+
action[0]
|
|
254
|
+
for action in self.current_task_state.task.get_actions(
|
|
255
|
+
self.content_object, user
|
|
256
|
+
)
|
|
257
|
+
]
|
|
258
|
+
)
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
def update(self, user=None, next_task=None):
|
|
262
|
+
"""
|
|
263
|
+
Checks the status of the current task, and progresses (or ends) the workflow if appropriate.
|
|
264
|
+
If the workflow progresses, next_task will be used to start a specific task next if provided.
|
|
265
|
+
"""
|
|
266
|
+
if self.status != self.STATUS_IN_PROGRESS:
|
|
267
|
+
# Updating a completed or cancelled workflow should have no effect
|
|
268
|
+
return
|
|
269
|
+
try:
|
|
270
|
+
current_status = self.current_task_state.status
|
|
271
|
+
except AttributeError:
|
|
272
|
+
current_status = None
|
|
273
|
+
if current_status == TaskState.STATUS_REJECTED:
|
|
274
|
+
self.status = self.STATUS_NEEDS_CHANGES
|
|
275
|
+
self.save()
|
|
276
|
+
workflow_rejected.send(sender=self.__class__, instance=self, user=user)
|
|
277
|
+
else:
|
|
278
|
+
if not next_task:
|
|
279
|
+
next_task = self.get_next_task()
|
|
280
|
+
if next_task:
|
|
281
|
+
if (
|
|
282
|
+
(not self.current_task_state)
|
|
283
|
+
or self.current_task_state.status
|
|
284
|
+
!= self.current_task_state.STATUS_IN_PROGRESS
|
|
285
|
+
):
|
|
286
|
+
# if not on a task, or the next task to move to is not the current task (ie current task's status is
|
|
287
|
+
# not STATUS_IN_PROGRESS), move to the next task
|
|
288
|
+
self.current_task_state = next_task.specific.start(self, user=user)
|
|
289
|
+
self.save()
|
|
290
|
+
# if task has auto-approved, update the workflow again
|
|
291
|
+
if (
|
|
292
|
+
self.current_task_state.status
|
|
293
|
+
!= self.current_task_state.STATUS_IN_PROGRESS
|
|
294
|
+
):
|
|
295
|
+
self.update(user=user)
|
|
296
|
+
# otherwise, continue on the current task
|
|
297
|
+
else:
|
|
298
|
+
# if there is no uncompleted task, finish the workflow.
|
|
299
|
+
self.finish(user=user)
|
|
300
|
+
|
|
301
|
+
@property
|
|
302
|
+
def successful_task_states(self):
|
|
303
|
+
successful_task_states = self.task_states.filter(
|
|
304
|
+
Q(status=TaskState.STATUS_APPROVED) | Q(status=TaskState.STATUS_SKIPPED)
|
|
305
|
+
)
|
|
306
|
+
if getattr(settings, "WAGTAIL_WORKFLOW_REQUIRE_REAPPROVAL_ON_EDIT", False):
|
|
307
|
+
successful_task_states = successful_task_states.filter(
|
|
308
|
+
revision=self.content_object.get_latest_revision()
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
return successful_task_states
|
|
312
|
+
|
|
313
|
+
def get_next_task(self):
|
|
314
|
+
"""
|
|
315
|
+
Returns the next active task, which has not been either approved or skipped.
|
|
316
|
+
"""
|
|
317
|
+
return (
|
|
318
|
+
Task.objects.filter(workflow_tasks__workflow=self.workflow, active=True)
|
|
319
|
+
.exclude(task_states__in=self.successful_task_states)
|
|
320
|
+
.order_by("workflow_tasks__sort_order")
|
|
321
|
+
.first()
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
def cancel(self, user=None):
|
|
325
|
+
"""Cancels the workflow state"""
|
|
326
|
+
from wagtail.models import Page
|
|
327
|
+
|
|
328
|
+
if self.status not in (self.STATUS_IN_PROGRESS, self.STATUS_NEEDS_CHANGES):
|
|
329
|
+
raise PermissionDenied
|
|
330
|
+
self.status = self.STATUS_CANCELLED
|
|
331
|
+
self.save()
|
|
332
|
+
|
|
333
|
+
instance = self.content_object
|
|
334
|
+
if isinstance(instance, Page):
|
|
335
|
+
instance = self.content_object.specific
|
|
336
|
+
|
|
337
|
+
log(
|
|
338
|
+
instance=instance,
|
|
339
|
+
action="wagtail.workflow.cancel",
|
|
340
|
+
data={
|
|
341
|
+
"workflow": {
|
|
342
|
+
"id": self.workflow_id,
|
|
343
|
+
"title": self.workflow.name,
|
|
344
|
+
"status": self.status,
|
|
345
|
+
"task_state_id": self.current_task_state.id,
|
|
346
|
+
"task": {
|
|
347
|
+
"id": self.current_task_state.task.id,
|
|
348
|
+
"title": self.current_task_state.task.name,
|
|
349
|
+
},
|
|
350
|
+
}
|
|
351
|
+
},
|
|
352
|
+
revision=self.current_task_state.revision,
|
|
353
|
+
user=user,
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
for state in self.task_states.filter(status=TaskState.STATUS_IN_PROGRESS):
|
|
357
|
+
# Cancel all in progress task states
|
|
358
|
+
state.specific.cancel(user=user)
|
|
359
|
+
workflow_cancelled.send(sender=self.__class__, instance=self, user=user)
|
|
360
|
+
|
|
361
|
+
@transaction.atomic
|
|
362
|
+
def finish(self, user=None):
|
|
363
|
+
"""
|
|
364
|
+
Finishes a successful in progress workflow, marking it as approved and performing the ``on_finish`` action.
|
|
365
|
+
"""
|
|
366
|
+
if self.status != self.STATUS_IN_PROGRESS:
|
|
367
|
+
raise PermissionDenied
|
|
368
|
+
self.status = self.STATUS_APPROVED
|
|
369
|
+
self.save()
|
|
370
|
+
self.on_finish(user=user)
|
|
371
|
+
workflow_approved.send(sender=self.__class__, instance=self, user=user)
|
|
372
|
+
|
|
373
|
+
def copy_approved_task_states_to_revision(self, revision):
|
|
374
|
+
"""
|
|
375
|
+
Creates copies of previously approved task states with revision set to a different revision.
|
|
376
|
+
"""
|
|
377
|
+
approved_states = TaskState.objects.filter(
|
|
378
|
+
workflow_state=self, status=TaskState.STATUS_APPROVED
|
|
379
|
+
)
|
|
380
|
+
for state in approved_states:
|
|
381
|
+
state.copy(update_attrs={"revision": revision})
|
|
382
|
+
|
|
383
|
+
def revisions(self):
|
|
384
|
+
"""
|
|
385
|
+
Returns all revisions associated with task states linked to the current workflow state.
|
|
386
|
+
"""
|
|
387
|
+
return Revision.objects.filter(
|
|
388
|
+
base_content_type_id=self.base_content_type_id,
|
|
389
|
+
object_id=self.object_id,
|
|
390
|
+
id__in=self.task_states.values_list("revision_id", flat=True),
|
|
391
|
+
).defer("content")
|
|
392
|
+
|
|
393
|
+
def _get_applicable_task_states(self):
|
|
394
|
+
"""
|
|
395
|
+
Returns the set of task states whose status applies to the current revision.
|
|
396
|
+
"""
|
|
397
|
+
task_states = TaskState.objects.filter(workflow_state_id=self.id)
|
|
398
|
+
# If WAGTAIL_WORKFLOW_REQUIRE_REAPPROVAL_ON_EDIT=True, this is only task states created on the current revision
|
|
399
|
+
if getattr(settings, "WAGTAIL_WORKFLOW_REQUIRE_REAPPROVAL_ON_EDIT", False):
|
|
400
|
+
latest_revision_id = (
|
|
401
|
+
self.revisions()
|
|
402
|
+
.order_by("-created_at", "-id")
|
|
403
|
+
.values_list("id", flat=True)
|
|
404
|
+
.first()
|
|
405
|
+
)
|
|
406
|
+
task_states = task_states.filter(revision_id=latest_revision_id)
|
|
407
|
+
return task_states
|
|
408
|
+
|
|
409
|
+
def all_tasks_with_status(self):
|
|
410
|
+
"""
|
|
411
|
+
Returns a list of Task objects that are linked with this workflow state's
|
|
412
|
+
workflow. The status of that task in this workflow state is annotated in the
|
|
413
|
+
``.status`` field. And a displayable version of that status is annotated in the
|
|
414
|
+
``.status_display`` field.
|
|
415
|
+
|
|
416
|
+
This is different to querying TaskState as it also returns tasks that haven't
|
|
417
|
+
been started yet (so won't have a TaskState).
|
|
418
|
+
"""
|
|
419
|
+
# Get the set of task states whose status applies to the current revision
|
|
420
|
+
task_states = self._get_applicable_task_states()
|
|
421
|
+
|
|
422
|
+
tasks = list(
|
|
423
|
+
self.workflow.tasks.annotate(
|
|
424
|
+
status=Subquery(
|
|
425
|
+
task_states.filter(
|
|
426
|
+
task_id=OuterRef("id"),
|
|
427
|
+
)
|
|
428
|
+
.order_by("-started_at", "-id")
|
|
429
|
+
.values("status")[:1]
|
|
430
|
+
),
|
|
431
|
+
)
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
# Manually annotate status_display
|
|
435
|
+
status_choices = dict(TaskState.STATUS_CHOICES)
|
|
436
|
+
for task in tasks:
|
|
437
|
+
task.status_display = status_choices.get(task.status, _("Not started"))
|
|
438
|
+
|
|
439
|
+
return tasks
|
|
440
|
+
|
|
441
|
+
def all_tasks_with_state(self):
|
|
442
|
+
"""
|
|
443
|
+
Returns a list of Task objects that are linked with this WorkflowState's
|
|
444
|
+
workflow, and have the latest task state.
|
|
445
|
+
|
|
446
|
+
In a "Submit for moderation -> reject at step 1 -> resubmit -> accept" workflow, this ensures
|
|
447
|
+
the task list reflects the accept, rather than the reject.
|
|
448
|
+
"""
|
|
449
|
+
task_states = self._get_applicable_task_states()
|
|
450
|
+
|
|
451
|
+
tasks = list(
|
|
452
|
+
self.workflow.tasks.annotate(
|
|
453
|
+
task_state_id=Subquery(
|
|
454
|
+
task_states.filter(
|
|
455
|
+
task_id=OuterRef("id"),
|
|
456
|
+
)
|
|
457
|
+
.order_by("-started_at", "-id")
|
|
458
|
+
.values("id")[:1]
|
|
459
|
+
),
|
|
460
|
+
)
|
|
461
|
+
)
|
|
462
|
+
|
|
463
|
+
task_states = {task_state.id: task_state for task_state in task_states}
|
|
464
|
+
# Manually annotate task_state
|
|
465
|
+
for task in tasks:
|
|
466
|
+
task.task_state = task_states.get(task.task_state_id)
|
|
467
|
+
|
|
468
|
+
return tasks
|
|
469
|
+
|
|
470
|
+
@property
|
|
471
|
+
def is_active(self):
|
|
472
|
+
return self.status not in [self.STATUS_APPROVED, self.STATUS_CANCELLED]
|
|
473
|
+
|
|
474
|
+
@property
|
|
475
|
+
def is_at_final_task(self):
|
|
476
|
+
"""
|
|
477
|
+
Returns the next active task, which has not been either approved or skipped.
|
|
478
|
+
"""
|
|
479
|
+
last_task = (
|
|
480
|
+
Task.objects.filter(workflow_tasks__workflow=self.workflow, active=True)
|
|
481
|
+
.exclude(task_states__in=self.successful_task_states)
|
|
482
|
+
.order_by("workflow_tasks__sort_order")
|
|
483
|
+
.last()
|
|
484
|
+
)
|
|
485
|
+
|
|
486
|
+
return self.get_next_task() == last_task
|
|
487
|
+
|
|
488
|
+
class Meta:
|
|
489
|
+
verbose_name = _("Workflow state")
|
|
490
|
+
verbose_name_plural = _("Workflow states")
|
|
491
|
+
# prevent multiple STATUS_IN_PROGRESS/STATUS_NEEDS_CHANGES workflows for the same object. This is only supported by specific databases (e.g. Postgres, SQL Server), so is checked additionally on save.
|
|
492
|
+
constraints = [
|
|
493
|
+
models.UniqueConstraint(
|
|
494
|
+
fields=["base_content_type", "object_id"],
|
|
495
|
+
condition=Q(status__in=("in_progress", "needs_changes")),
|
|
496
|
+
name="unique_in_progress_workflow",
|
|
497
|
+
)
|
|
498
|
+
]
|
|
499
|
+
indexes = [
|
|
500
|
+
models.Index(
|
|
501
|
+
fields=["content_type", "object_id"],
|
|
502
|
+
name="workflowstate_ct_id_idx",
|
|
503
|
+
),
|
|
504
|
+
models.Index(
|
|
505
|
+
fields=["base_content_type", "object_id"],
|
|
506
|
+
name="workflowstate_base_ct_id_idx",
|
|
507
|
+
),
|
|
508
|
+
]
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
class WorkflowManager(models.Manager):
|
|
512
|
+
def active(self):
|
|
513
|
+
return self.filter(active=True)
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
class AbstractWorkflow(ClusterableModel):
|
|
517
|
+
name = models.CharField(max_length=255, verbose_name=_("name"))
|
|
518
|
+
active = models.BooleanField(
|
|
519
|
+
verbose_name=_("active"),
|
|
520
|
+
default=True,
|
|
521
|
+
help_text=_(
|
|
522
|
+
"Active workflows can be added to pages/snippets. Deactivating a workflow does not remove it from existing pages/snippets."
|
|
523
|
+
),
|
|
524
|
+
)
|
|
525
|
+
objects = WorkflowManager()
|
|
526
|
+
|
|
527
|
+
def __str__(self):
|
|
528
|
+
return self.name
|
|
529
|
+
|
|
530
|
+
@property
|
|
531
|
+
def tasks(self):
|
|
532
|
+
"""
|
|
533
|
+
Returns all ``Task`` instances linked to this workflow.
|
|
534
|
+
"""
|
|
535
|
+
return Task.objects.filter(workflow_tasks__workflow=self).order_by(
|
|
536
|
+
"workflow_tasks__sort_order"
|
|
537
|
+
)
|
|
538
|
+
|
|
539
|
+
@transaction.atomic
|
|
540
|
+
def start(self, obj, user):
|
|
541
|
+
"""
|
|
542
|
+
Initiates a workflow by creating an instance of ``WorkflowState``.
|
|
543
|
+
"""
|
|
544
|
+
|
|
545
|
+
state = WorkflowState(
|
|
546
|
+
content_type=obj.get_content_type(),
|
|
547
|
+
base_content_type=obj.get_base_content_type(),
|
|
548
|
+
object_id=str(obj.pk),
|
|
549
|
+
workflow=self,
|
|
550
|
+
status=WorkflowState.STATUS_IN_PROGRESS,
|
|
551
|
+
requested_by=user,
|
|
552
|
+
)
|
|
553
|
+
state.save()
|
|
554
|
+
state.update(user=user)
|
|
555
|
+
workflow_submitted.send(sender=state.__class__, instance=state, user=user)
|
|
556
|
+
|
|
557
|
+
next_task_data = None
|
|
558
|
+
if state.current_task_state:
|
|
559
|
+
next_task_data = {
|
|
560
|
+
"id": state.current_task_state.task.id,
|
|
561
|
+
"title": state.current_task_state.task.name,
|
|
562
|
+
}
|
|
563
|
+
log(
|
|
564
|
+
instance=obj,
|
|
565
|
+
action="wagtail.workflow.start",
|
|
566
|
+
data={
|
|
567
|
+
"workflow": {
|
|
568
|
+
"id": self.id,
|
|
569
|
+
"title": self.name,
|
|
570
|
+
"status": state.status,
|
|
571
|
+
"next": next_task_data,
|
|
572
|
+
"task_state_id": state.current_task_state.id
|
|
573
|
+
if state.current_task_state
|
|
574
|
+
else None,
|
|
575
|
+
}
|
|
576
|
+
},
|
|
577
|
+
revision=obj.get_latest_revision(),
|
|
578
|
+
user=user,
|
|
579
|
+
)
|
|
580
|
+
|
|
581
|
+
return state
|
|
582
|
+
|
|
583
|
+
@transaction.atomic
|
|
584
|
+
def deactivate(self, user=None):
|
|
585
|
+
"""
|
|
586
|
+
Sets the workflow as inactive, and cancels all in progress instances of ``WorkflowState`` linked to this workflow.
|
|
587
|
+
"""
|
|
588
|
+
from wagtail.models import WorkflowPage
|
|
589
|
+
|
|
590
|
+
self.active = False
|
|
591
|
+
in_progress_states = WorkflowState.objects.filter(
|
|
592
|
+
workflow=self, status=WorkflowState.STATUS_IN_PROGRESS
|
|
593
|
+
)
|
|
594
|
+
for state in in_progress_states:
|
|
595
|
+
state.cancel(user=user)
|
|
596
|
+
WorkflowPage.objects.filter(workflow=self).delete()
|
|
597
|
+
WorkflowContentType.objects.filter(workflow=self).delete()
|
|
598
|
+
self.save()
|
|
599
|
+
|
|
600
|
+
def all_pages(self):
|
|
601
|
+
"""
|
|
602
|
+
Returns a queryset of all the pages that this Workflow applies to.
|
|
603
|
+
"""
|
|
604
|
+
from wagtail.models import Page
|
|
605
|
+
|
|
606
|
+
pages = Page.objects.none()
|
|
607
|
+
|
|
608
|
+
for workflow_page in self.workflow_pages.all():
|
|
609
|
+
pages |= workflow_page.get_pages()
|
|
610
|
+
|
|
611
|
+
return pages
|
|
612
|
+
|
|
613
|
+
class Meta:
|
|
614
|
+
verbose_name = _("workflow")
|
|
615
|
+
verbose_name_plural = _("workflows")
|
|
616
|
+
abstract = True
|
|
617
|
+
|
|
618
|
+
|
|
619
|
+
class Workflow(AbstractWorkflow):
|
|
620
|
+
pass
|
|
621
|
+
|
|
622
|
+
|
|
623
|
+
class WorkflowTask(Orderable):
|
|
624
|
+
workflow = ParentalKey(
|
|
625
|
+
"Workflow",
|
|
626
|
+
on_delete=models.CASCADE,
|
|
627
|
+
verbose_name=_("workflow_tasks"),
|
|
628
|
+
related_name="workflow_tasks",
|
|
629
|
+
)
|
|
630
|
+
task = models.ForeignKey(
|
|
631
|
+
"Task",
|
|
632
|
+
on_delete=models.CASCADE,
|
|
633
|
+
verbose_name=_("task"),
|
|
634
|
+
related_name="workflow_tasks",
|
|
635
|
+
limit_choices_to={"active": True},
|
|
636
|
+
)
|
|
637
|
+
|
|
638
|
+
class Meta(Orderable.Meta):
|
|
639
|
+
unique_together = [("workflow", "task")]
|
|
640
|
+
verbose_name = _("workflow task order")
|
|
641
|
+
verbose_name_plural = _("workflow task orders")
|
|
642
|
+
|
|
643
|
+
|
|
644
|
+
class TaskQuerySet(SpecificQuerySetMixin, models.QuerySet):
|
|
645
|
+
def active(self):
|
|
646
|
+
return self.filter(active=True)
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
TaskManager = models.Manager.from_queryset(TaskQuerySet)
|
|
650
|
+
|
|
651
|
+
|
|
652
|
+
class Task(SpecificMixin, models.Model):
|
|
653
|
+
name = models.CharField(max_length=255, verbose_name=_("name"))
|
|
654
|
+
content_type = models.ForeignKey(
|
|
655
|
+
ContentType,
|
|
656
|
+
verbose_name=_("content type"),
|
|
657
|
+
related_name="wagtail_tasks",
|
|
658
|
+
on_delete=models.CASCADE,
|
|
659
|
+
)
|
|
660
|
+
active = models.BooleanField(
|
|
661
|
+
verbose_name=_("active"),
|
|
662
|
+
default=True,
|
|
663
|
+
help_text=_(
|
|
664
|
+
"Active tasks can be added to workflows. Deactivating a task does not remove it from existing workflows."
|
|
665
|
+
),
|
|
666
|
+
)
|
|
667
|
+
objects = TaskManager()
|
|
668
|
+
|
|
669
|
+
admin_form_fields = ["name"]
|
|
670
|
+
admin_form_readonly_on_edit_fields = ["name"]
|
|
671
|
+
|
|
672
|
+
def __init__(self, *args, **kwargs):
|
|
673
|
+
super().__init__(*args, **kwargs)
|
|
674
|
+
if not self.id:
|
|
675
|
+
# this model is being newly created
|
|
676
|
+
# rather than retrieved from the db;
|
|
677
|
+
if not self.content_type_id:
|
|
678
|
+
# set content type to correctly represent the model class
|
|
679
|
+
# that this was created as
|
|
680
|
+
self.content_type = ContentType.objects.get_for_model(self)
|
|
681
|
+
|
|
682
|
+
def __str__(self):
|
|
683
|
+
return self.name
|
|
684
|
+
|
|
685
|
+
@property
|
|
686
|
+
def workflows(self):
|
|
687
|
+
"""
|
|
688
|
+
Returns all ``Workflow`` instances that use this task.
|
|
689
|
+
"""
|
|
690
|
+
return Workflow.objects.filter(workflow_tasks__task=self)
|
|
691
|
+
|
|
692
|
+
@property
|
|
693
|
+
def active_workflows(self):
|
|
694
|
+
"""
|
|
695
|
+
Return a ``QuerySet``` of active workflows that this task is part of.
|
|
696
|
+
"""
|
|
697
|
+
return Workflow.objects.active().filter(workflow_tasks__task=self)
|
|
698
|
+
|
|
699
|
+
@classmethod
|
|
700
|
+
def get_verbose_name(cls):
|
|
701
|
+
"""
|
|
702
|
+
Returns the human-readable "verbose name" of this task model e.g "Group approval task".
|
|
703
|
+
"""
|
|
704
|
+
# This is similar to doing cls._meta.verbose_name.title()
|
|
705
|
+
# except this doesn't convert any characters to lowercase
|
|
706
|
+
return capfirst(cls._meta.verbose_name)
|
|
707
|
+
|
|
708
|
+
task_state_class = None
|
|
709
|
+
|
|
710
|
+
@classmethod
|
|
711
|
+
def get_task_state_class(self):
|
|
712
|
+
return self.task_state_class or TaskState
|
|
713
|
+
|
|
714
|
+
def start(self, workflow_state, user=None):
|
|
715
|
+
"""
|
|
716
|
+
Start this task on the provided workflow state by creating an instance of TaskState.
|
|
717
|
+
"""
|
|
718
|
+
task_state = self.get_task_state_class()(workflow_state=workflow_state)
|
|
719
|
+
task_state.status = TaskState.STATUS_IN_PROGRESS
|
|
720
|
+
task_state.revision = workflow_state.content_object.get_latest_revision()
|
|
721
|
+
task_state.task = self
|
|
722
|
+
task_state.save()
|
|
723
|
+
task_submitted.send(
|
|
724
|
+
sender=task_state.specific.__class__,
|
|
725
|
+
instance=task_state.specific,
|
|
726
|
+
user=user,
|
|
727
|
+
)
|
|
728
|
+
return task_state
|
|
729
|
+
|
|
730
|
+
@transaction.atomic
|
|
731
|
+
def on_action(self, task_state, user, action_name, **kwargs):
|
|
732
|
+
"""
|
|
733
|
+
Performs an action on a task state determined by the ``action_name`` string passed.
|
|
734
|
+
"""
|
|
735
|
+
if action_name == "approve":
|
|
736
|
+
task_state.approve(user=user, **kwargs)
|
|
737
|
+
elif action_name == "reject":
|
|
738
|
+
task_state.reject(user=user, **kwargs)
|
|
739
|
+
|
|
740
|
+
def user_can_access_editor(self, obj, user):
|
|
741
|
+
"""
|
|
742
|
+
Returns ``True`` if a user who would not normally be able to access the editor for the
|
|
743
|
+
object should be able to if the object is currently on this task.
|
|
744
|
+
Note that returning ``False`` does not remove permissions from users who would otherwise have them.
|
|
745
|
+
"""
|
|
746
|
+
return False
|
|
747
|
+
|
|
748
|
+
def locked_for_user(self, obj, user):
|
|
749
|
+
"""
|
|
750
|
+
Returns ``True`` if the object should be locked to a given user's edits.
|
|
751
|
+
This can be used to prevent editing by non-reviewers.
|
|
752
|
+
"""
|
|
753
|
+
return False
|
|
754
|
+
|
|
755
|
+
def user_can_lock(self, obj, user):
|
|
756
|
+
"""
|
|
757
|
+
Returns ``True`` if a user who would not normally be able to lock the object should be able to
|
|
758
|
+
if the object is currently on this task.
|
|
759
|
+
Note that returning ``False`` does not remove permissions from users who would otherwise have them.
|
|
760
|
+
"""
|
|
761
|
+
return False
|
|
762
|
+
|
|
763
|
+
def user_can_unlock(self, obj, user):
|
|
764
|
+
"""
|
|
765
|
+
Returns ``True`` if a user who would not normally be able to unlock the object should be able to
|
|
766
|
+
if the object is currently on this task.
|
|
767
|
+
Note that returning ``False`` does not remove permissions from users who would otherwise have them.
|
|
768
|
+
"""
|
|
769
|
+
return False
|
|
770
|
+
|
|
771
|
+
def get_actions(self, obj, user):
|
|
772
|
+
"""
|
|
773
|
+
Get the list of action strings (name, verbose_name, whether the action requires additional data - see
|
|
774
|
+
``get_form_for_action``) for actions the current user can perform for this task on the given object.
|
|
775
|
+
These strings should be the same as those able to be passed to ``on_action``.
|
|
776
|
+
"""
|
|
777
|
+
return []
|
|
778
|
+
|
|
779
|
+
def get_form_for_action(self, action):
|
|
780
|
+
return TaskStateCommentForm
|
|
781
|
+
|
|
782
|
+
def get_template_for_action(self, action):
|
|
783
|
+
"""
|
|
784
|
+
Specifies a template for the workflow action modal.
|
|
785
|
+
"""
|
|
786
|
+
return ""
|
|
787
|
+
|
|
788
|
+
def get_task_states_user_can_moderate(self, user, **kwargs):
|
|
789
|
+
"""Returns a ``QuerySet`` of the task states the current user can moderate"""
|
|
790
|
+
return TaskState.objects.none()
|
|
791
|
+
|
|
792
|
+
@classmethod
|
|
793
|
+
def get_description(cls):
|
|
794
|
+
"""
|
|
795
|
+
Returns the task description.
|
|
796
|
+
"""
|
|
797
|
+
return ""
|
|
798
|
+
|
|
799
|
+
@transaction.atomic
|
|
800
|
+
def deactivate(self, user=None):
|
|
801
|
+
"""
|
|
802
|
+
Set ``active`` to False and cancel all in progress task states linked to this task.
|
|
803
|
+
"""
|
|
804
|
+
self.active = False
|
|
805
|
+
self.save()
|
|
806
|
+
in_progress_states = TaskState.objects.filter(
|
|
807
|
+
task=self, status=TaskState.STATUS_IN_PROGRESS
|
|
808
|
+
)
|
|
809
|
+
for state in in_progress_states:
|
|
810
|
+
state.cancel(user=user)
|
|
811
|
+
|
|
812
|
+
class Meta:
|
|
813
|
+
verbose_name = _("task")
|
|
814
|
+
verbose_name_plural = _("tasks")
|
|
815
|
+
|
|
816
|
+
|
|
817
|
+
class AbstractGroupApprovalTask(Task):
|
|
818
|
+
groups = models.ManyToManyField(
|
|
819
|
+
Group,
|
|
820
|
+
verbose_name=_("groups"),
|
|
821
|
+
help_text=_(
|
|
822
|
+
"Pages/snippets at this step in a workflow will be moderated or approved by these groups of users"
|
|
823
|
+
),
|
|
824
|
+
)
|
|
825
|
+
|
|
826
|
+
admin_form_fields = Task.admin_form_fields + ["groups"]
|
|
827
|
+
admin_form_widgets = {
|
|
828
|
+
"groups": forms.CheckboxSelectMultiple,
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
def start(self, workflow_state, user=None):
|
|
832
|
+
if (
|
|
833
|
+
isinstance(workflow_state.content_object, LockableMixin)
|
|
834
|
+
and workflow_state.content_object.locked_by
|
|
835
|
+
):
|
|
836
|
+
# If the person who locked the object isn't in one of the groups, unlock the object
|
|
837
|
+
if not workflow_state.content_object.locked_by.groups.filter(
|
|
838
|
+
id__in=self.groups.all()
|
|
839
|
+
).exists():
|
|
840
|
+
workflow_state.content_object.locked = False
|
|
841
|
+
workflow_state.content_object.locked_by = None
|
|
842
|
+
workflow_state.content_object.locked_at = None
|
|
843
|
+
workflow_state.content_object.save(
|
|
844
|
+
update_fields=["locked", "locked_by", "locked_at"]
|
|
845
|
+
)
|
|
846
|
+
|
|
847
|
+
return super().start(workflow_state, user=user)
|
|
848
|
+
|
|
849
|
+
def _user_in_groups(self, user):
|
|
850
|
+
# Cache the check whether "this user is in any of this
|
|
851
|
+
# GroupApprovalTask's groups" on the user object, in case we do it
|
|
852
|
+
# against the same user and task multiple times in a request.
|
|
853
|
+
# Use a dict to map the task id to the check result, in case we also
|
|
854
|
+
# check against different GroupApprovalTasks for the same user.
|
|
855
|
+
cache_attr = "_group_approval_task_checks"
|
|
856
|
+
if not (checks_cache := getattr(user, cache_attr, {})):
|
|
857
|
+
setattr(user, cache_attr, checks_cache)
|
|
858
|
+
|
|
859
|
+
if self.pk not in checks_cache:
|
|
860
|
+
checks_cache[self.pk] = self.groups.filter(
|
|
861
|
+
id__in=user.groups.all()
|
|
862
|
+
).exists()
|
|
863
|
+
|
|
864
|
+
return checks_cache[self.pk]
|
|
865
|
+
|
|
866
|
+
def user_can_access_editor(self, obj, user):
|
|
867
|
+
return user.is_superuser or self._user_in_groups(user)
|
|
868
|
+
|
|
869
|
+
def locked_for_user(self, obj, user):
|
|
870
|
+
return not (user.is_superuser or self._user_in_groups(user))
|
|
871
|
+
|
|
872
|
+
def user_can_lock(self, obj, user):
|
|
873
|
+
return self._user_in_groups(user)
|
|
874
|
+
|
|
875
|
+
def user_can_unlock(self, obj, user):
|
|
876
|
+
return False
|
|
877
|
+
|
|
878
|
+
def get_actions(self, obj, user):
|
|
879
|
+
if user.is_superuser or self._user_in_groups(user):
|
|
880
|
+
return [
|
|
881
|
+
("reject", _("Request changes"), True),
|
|
882
|
+
("approve", _("Approve"), False),
|
|
883
|
+
("approve", _("Approve with comment"), True),
|
|
884
|
+
]
|
|
885
|
+
|
|
886
|
+
return []
|
|
887
|
+
|
|
888
|
+
def get_task_states_user_can_moderate(self, user, **kwargs):
|
|
889
|
+
if user.is_superuser or self._user_in_groups(user):
|
|
890
|
+
return self.task_states.filter(status=TaskState.STATUS_IN_PROGRESS)
|
|
891
|
+
else:
|
|
892
|
+
return TaskState.objects.none()
|
|
893
|
+
|
|
894
|
+
@classmethod
|
|
895
|
+
def get_description(cls):
|
|
896
|
+
return _("Members of the chosen Wagtail Groups can approve this task")
|
|
897
|
+
|
|
898
|
+
class Meta:
|
|
899
|
+
abstract = True
|
|
900
|
+
verbose_name = _("Group approval task")
|
|
901
|
+
verbose_name_plural = _("Group approval tasks")
|
|
902
|
+
|
|
903
|
+
|
|
904
|
+
class GroupApprovalTask(AbstractGroupApprovalTask):
|
|
905
|
+
pass
|
|
906
|
+
|
|
907
|
+
|
|
908
|
+
class BaseTaskStateManager(models.Manager):
|
|
909
|
+
def reviewable_by(self, user):
|
|
910
|
+
tasks = Task.objects.filter(active=True).specific()
|
|
911
|
+
states = TaskState.objects.none()
|
|
912
|
+
for task in tasks:
|
|
913
|
+
states = states | task.get_task_states_user_can_moderate(user=user)
|
|
914
|
+
return states
|
|
915
|
+
|
|
916
|
+
|
|
917
|
+
class TaskStateQuerySet(SpecificQuerySetMixin, models.QuerySet):
|
|
918
|
+
def for_instance(self, instance):
|
|
919
|
+
"""
|
|
920
|
+
Filters to only TaskStates for the given instance
|
|
921
|
+
"""
|
|
922
|
+
try:
|
|
923
|
+
# Use RevisionMixin.get_base_content_type() if available
|
|
924
|
+
return self.filter(
|
|
925
|
+
workflow_state__base_content_type=instance.get_base_content_type(),
|
|
926
|
+
workflow_state__object_id=str(instance.pk),
|
|
927
|
+
)
|
|
928
|
+
except AttributeError:
|
|
929
|
+
# Fallback to ContentType for the model
|
|
930
|
+
return self.filter(
|
|
931
|
+
workflow_state__content_type=ContentType.objects.get_for_model(
|
|
932
|
+
instance, for_concrete_model=False
|
|
933
|
+
),
|
|
934
|
+
workflow_state__object_id=str(instance.pk),
|
|
935
|
+
)
|
|
936
|
+
|
|
937
|
+
|
|
938
|
+
TaskStateManager = BaseTaskStateManager.from_queryset(TaskStateQuerySet)
|
|
939
|
+
|
|
940
|
+
|
|
941
|
+
class TaskState(SpecificMixin, models.Model):
|
|
942
|
+
"""Tracks the status of a given Task for a particular revision."""
|
|
943
|
+
|
|
944
|
+
STATUS_IN_PROGRESS = "in_progress"
|
|
945
|
+
STATUS_APPROVED = "approved"
|
|
946
|
+
STATUS_REJECTED = "rejected"
|
|
947
|
+
STATUS_SKIPPED = "skipped"
|
|
948
|
+
STATUS_CANCELLED = "cancelled"
|
|
949
|
+
STATUS_CHOICES = (
|
|
950
|
+
(STATUS_IN_PROGRESS, _("In progress")),
|
|
951
|
+
(STATUS_APPROVED, _("Approved")),
|
|
952
|
+
(STATUS_REJECTED, _("Rejected")),
|
|
953
|
+
(STATUS_SKIPPED, _("Skipped")),
|
|
954
|
+
(STATUS_CANCELLED, _("Cancelled")),
|
|
955
|
+
)
|
|
956
|
+
|
|
957
|
+
workflow_state = models.ForeignKey(
|
|
958
|
+
"WorkflowState",
|
|
959
|
+
on_delete=models.CASCADE,
|
|
960
|
+
verbose_name=_("workflow state"),
|
|
961
|
+
related_name="task_states",
|
|
962
|
+
)
|
|
963
|
+
revision = models.ForeignKey(
|
|
964
|
+
"Revision",
|
|
965
|
+
on_delete=models.CASCADE,
|
|
966
|
+
verbose_name=_("revision"),
|
|
967
|
+
related_name="task_states",
|
|
968
|
+
)
|
|
969
|
+
task = models.ForeignKey(
|
|
970
|
+
"Task",
|
|
971
|
+
on_delete=models.CASCADE,
|
|
972
|
+
verbose_name=_("task"),
|
|
973
|
+
related_name="task_states",
|
|
974
|
+
)
|
|
975
|
+
status = models.fields.CharField(
|
|
976
|
+
choices=STATUS_CHOICES,
|
|
977
|
+
verbose_name=_("status"),
|
|
978
|
+
max_length=50,
|
|
979
|
+
default=STATUS_IN_PROGRESS,
|
|
980
|
+
)
|
|
981
|
+
started_at = models.DateTimeField(verbose_name=_("started at"), auto_now_add=True)
|
|
982
|
+
finished_at = models.DateTimeField(
|
|
983
|
+
verbose_name=_("finished at"), blank=True, null=True
|
|
984
|
+
)
|
|
985
|
+
finished_by = models.ForeignKey(
|
|
986
|
+
settings.AUTH_USER_MODEL,
|
|
987
|
+
verbose_name=_("finished by"),
|
|
988
|
+
null=True,
|
|
989
|
+
blank=True,
|
|
990
|
+
on_delete=models.SET_NULL,
|
|
991
|
+
related_name="finished_task_states",
|
|
992
|
+
)
|
|
993
|
+
comment = models.TextField(blank=True)
|
|
994
|
+
content_type = models.ForeignKey(
|
|
995
|
+
ContentType,
|
|
996
|
+
verbose_name=_("content type"),
|
|
997
|
+
related_name="wagtail_task_states",
|
|
998
|
+
on_delete=models.CASCADE,
|
|
999
|
+
)
|
|
1000
|
+
exclude_fields_in_copy = []
|
|
1001
|
+
default_exclude_fields_in_copy = ["id"]
|
|
1002
|
+
|
|
1003
|
+
objects = TaskStateManager()
|
|
1004
|
+
|
|
1005
|
+
def __init__(self, *args, **kwargs):
|
|
1006
|
+
super().__init__(*args, **kwargs)
|
|
1007
|
+
if not self.id:
|
|
1008
|
+
# this model is being newly created
|
|
1009
|
+
# rather than retrieved from the db;
|
|
1010
|
+
if not self.content_type_id:
|
|
1011
|
+
# set content type to correctly represent the model class
|
|
1012
|
+
# that this was created as
|
|
1013
|
+
self.content_type = ContentType.objects.get_for_model(self)
|
|
1014
|
+
|
|
1015
|
+
def __str__(self):
|
|
1016
|
+
return _("Task '%(task_name)s' on Revision '%(revision_info)s': %(status)s") % {
|
|
1017
|
+
"task_name": self.task,
|
|
1018
|
+
"revision_info": self.revision,
|
|
1019
|
+
"status": self.status,
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
@transaction.atomic
|
|
1023
|
+
def approve(self, user=None, update=True, comment=""):
|
|
1024
|
+
"""
|
|
1025
|
+
Approve the task state and update the workflow state.
|
|
1026
|
+
"""
|
|
1027
|
+
if self.status != self.STATUS_IN_PROGRESS:
|
|
1028
|
+
raise PermissionDenied
|
|
1029
|
+
self.status = self.STATUS_APPROVED
|
|
1030
|
+
self.finished_at = timezone.now()
|
|
1031
|
+
self.finished_by = user
|
|
1032
|
+
self.comment = comment
|
|
1033
|
+
self.save()
|
|
1034
|
+
|
|
1035
|
+
self.log_state_change_action(user, "approve")
|
|
1036
|
+
if update:
|
|
1037
|
+
self.workflow_state.update(user=user)
|
|
1038
|
+
task_approved.send(
|
|
1039
|
+
sender=self.specific.__class__, instance=self.specific, user=user
|
|
1040
|
+
)
|
|
1041
|
+
return self
|
|
1042
|
+
|
|
1043
|
+
@transaction.atomic
|
|
1044
|
+
def reject(self, user=None, update=True, comment=""):
|
|
1045
|
+
"""
|
|
1046
|
+
Reject the task state and update the workflow state.
|
|
1047
|
+
"""
|
|
1048
|
+
if self.status != self.STATUS_IN_PROGRESS:
|
|
1049
|
+
raise PermissionDenied
|
|
1050
|
+
self.status = self.STATUS_REJECTED
|
|
1051
|
+
self.finished_at = timezone.now()
|
|
1052
|
+
self.finished_by = user
|
|
1053
|
+
self.comment = comment
|
|
1054
|
+
self.save()
|
|
1055
|
+
|
|
1056
|
+
self.log_state_change_action(user, "reject")
|
|
1057
|
+
if update:
|
|
1058
|
+
self.workflow_state.update(user=user)
|
|
1059
|
+
task_rejected.send(
|
|
1060
|
+
sender=self.specific.__class__, instance=self.specific, user=user
|
|
1061
|
+
)
|
|
1062
|
+
|
|
1063
|
+
return self
|
|
1064
|
+
|
|
1065
|
+
@cached_property
|
|
1066
|
+
def task_type_started_at(self):
|
|
1067
|
+
"""
|
|
1068
|
+
Finds the first chronological started_at for successive TaskStates - ie started_at if the task had not been restarted.
|
|
1069
|
+
"""
|
|
1070
|
+
task_states = (
|
|
1071
|
+
TaskState.objects.filter(workflow_state=self.workflow_state)
|
|
1072
|
+
.order_by("-started_at")
|
|
1073
|
+
.select_related("task")
|
|
1074
|
+
)
|
|
1075
|
+
started_at = None
|
|
1076
|
+
for task_state in task_states:
|
|
1077
|
+
if task_state.task == self.task:
|
|
1078
|
+
started_at = task_state.started_at
|
|
1079
|
+
elif started_at:
|
|
1080
|
+
break
|
|
1081
|
+
return started_at
|
|
1082
|
+
|
|
1083
|
+
@transaction.atomic
|
|
1084
|
+
def cancel(self, user=None, resume=False, comment=""):
|
|
1085
|
+
"""
|
|
1086
|
+
Cancel the task state and update the workflow state.
|
|
1087
|
+
If ``resume`` is set to True, then upon update the workflow state is passed the current task as ``next_task``,
|
|
1088
|
+
causing it to start a new task state on the current task if possible.
|
|
1089
|
+
"""
|
|
1090
|
+
self.status = self.STATUS_CANCELLED
|
|
1091
|
+
self.finished_at = timezone.now()
|
|
1092
|
+
self.comment = comment
|
|
1093
|
+
self.finished_by = user
|
|
1094
|
+
self.save()
|
|
1095
|
+
if resume:
|
|
1096
|
+
self.workflow_state.update(user=user, next_task=self.task.specific)
|
|
1097
|
+
else:
|
|
1098
|
+
self.workflow_state.update(user=user)
|
|
1099
|
+
task_cancelled.send(
|
|
1100
|
+
sender=self.specific.__class__, instance=self.specific, user=user
|
|
1101
|
+
)
|
|
1102
|
+
return self
|
|
1103
|
+
|
|
1104
|
+
def copy(self, update_attrs=None, exclude_fields=None):
|
|
1105
|
+
"""
|
|
1106
|
+
Copy this task state, excluding the attributes in the ``exclude_fields`` list and updating any attributes
|
|
1107
|
+
to values specified in the ``update_attrs`` dictionary of ``attribute``: ``new value`` pairs.
|
|
1108
|
+
"""
|
|
1109
|
+
exclude_fields = (
|
|
1110
|
+
self.default_exclude_fields_in_copy
|
|
1111
|
+
+ self.exclude_fields_in_copy
|
|
1112
|
+
+ (exclude_fields or [])
|
|
1113
|
+
)
|
|
1114
|
+
instance, child_object_map = _copy(self.specific, exclude_fields, update_attrs)
|
|
1115
|
+
instance.save()
|
|
1116
|
+
_copy_m2m_relations(self, instance, exclude_fields=exclude_fields)
|
|
1117
|
+
return instance
|
|
1118
|
+
|
|
1119
|
+
def get_comment(self):
|
|
1120
|
+
"""
|
|
1121
|
+
Returns a string that is displayed in workflow history.
|
|
1122
|
+
|
|
1123
|
+
This could be a comment by the reviewer, or generated.
|
|
1124
|
+
Use mark_safe to return HTML.
|
|
1125
|
+
"""
|
|
1126
|
+
return self.comment
|
|
1127
|
+
|
|
1128
|
+
def log_state_change_action(self, user, action):
|
|
1129
|
+
"""Log the approval/rejection action"""
|
|
1130
|
+
obj = self.revision.as_object()
|
|
1131
|
+
next_task = self.workflow_state.get_next_task()
|
|
1132
|
+
next_task_data = None
|
|
1133
|
+
if next_task:
|
|
1134
|
+
next_task_data = {"id": next_task.id, "title": next_task.name}
|
|
1135
|
+
log(
|
|
1136
|
+
instance=obj,
|
|
1137
|
+
action=f"wagtail.workflow.{action}",
|
|
1138
|
+
user=user,
|
|
1139
|
+
data={
|
|
1140
|
+
"workflow": {
|
|
1141
|
+
"id": self.workflow_state.workflow.id,
|
|
1142
|
+
"title": self.workflow_state.workflow.name,
|
|
1143
|
+
"status": self.status,
|
|
1144
|
+
"task_state_id": self.id,
|
|
1145
|
+
"task": {
|
|
1146
|
+
"id": self.task.id,
|
|
1147
|
+
"title": self.task.name,
|
|
1148
|
+
},
|
|
1149
|
+
"next": next_task_data,
|
|
1150
|
+
},
|
|
1151
|
+
"comment": self.get_comment(),
|
|
1152
|
+
},
|
|
1153
|
+
revision=self.revision,
|
|
1154
|
+
)
|
|
1155
|
+
|
|
1156
|
+
class Meta:
|
|
1157
|
+
verbose_name = _("Task state")
|
|
1158
|
+
verbose_name_plural = _("Task states")
|
|
1159
|
+
|
|
1160
|
+
|
|
1161
|
+
class WorkflowMixin:
|
|
1162
|
+
"""A mixin that allows a model to have workflows."""
|
|
1163
|
+
|
|
1164
|
+
@classmethod
|
|
1165
|
+
def check(cls, **kwargs):
|
|
1166
|
+
return [
|
|
1167
|
+
*super().check(**kwargs),
|
|
1168
|
+
*cls._check_draftstate_and_revision_mixins(),
|
|
1169
|
+
]
|
|
1170
|
+
|
|
1171
|
+
@classmethod
|
|
1172
|
+
def _check_draftstate_and_revision_mixins(cls):
|
|
1173
|
+
mro = cls.mro()
|
|
1174
|
+
error = checks.Error(
|
|
1175
|
+
"WorkflowMixin requires DraftStateMixin and RevisionMixin (in that order).",
|
|
1176
|
+
hint=(
|
|
1177
|
+
"Make sure your model's inheritance order is as follows: "
|
|
1178
|
+
"WorkflowMixin, DraftStateMixin, RevisionMixin."
|
|
1179
|
+
),
|
|
1180
|
+
obj=cls,
|
|
1181
|
+
id="wagtailcore.E006",
|
|
1182
|
+
)
|
|
1183
|
+
|
|
1184
|
+
try:
|
|
1185
|
+
if not (
|
|
1186
|
+
mro.index(WorkflowMixin)
|
|
1187
|
+
< mro.index(DraftStateMixin)
|
|
1188
|
+
< mro.index(RevisionMixin)
|
|
1189
|
+
):
|
|
1190
|
+
return [error]
|
|
1191
|
+
except ValueError:
|
|
1192
|
+
return [error]
|
|
1193
|
+
|
|
1194
|
+
return []
|
|
1195
|
+
|
|
1196
|
+
@classmethod
|
|
1197
|
+
def get_default_workflow(cls):
|
|
1198
|
+
"""
|
|
1199
|
+
Returns the active workflow assigned to the model.
|
|
1200
|
+
|
|
1201
|
+
For non-``Page`` models, workflows are assigned to the model's content type,
|
|
1202
|
+
thus shared across all instances instead of being assigned to individual
|
|
1203
|
+
instances (unless :meth:`~WorkflowMixin.get_workflow` is overridden).
|
|
1204
|
+
|
|
1205
|
+
This method is used to determine the workflow to use when creating new
|
|
1206
|
+
instances of the model. On ``Page`` models, this method is unused as the
|
|
1207
|
+
workflow can be determined from the parent page's workflow.
|
|
1208
|
+
"""
|
|
1209
|
+
if not getattr(settings, "WAGTAIL_WORKFLOW_ENABLED", True):
|
|
1210
|
+
return None
|
|
1211
|
+
|
|
1212
|
+
content_type = ContentType.objects.get_for_model(cls, for_concrete_model=False)
|
|
1213
|
+
workflow_content_type = (
|
|
1214
|
+
WorkflowContentType.objects.filter(
|
|
1215
|
+
workflow__active=True,
|
|
1216
|
+
content_type=content_type,
|
|
1217
|
+
)
|
|
1218
|
+
.select_related("workflow")
|
|
1219
|
+
.first()
|
|
1220
|
+
)
|
|
1221
|
+
|
|
1222
|
+
if workflow_content_type:
|
|
1223
|
+
return workflow_content_type.workflow
|
|
1224
|
+
return None
|
|
1225
|
+
|
|
1226
|
+
@property
|
|
1227
|
+
def has_workflow(self):
|
|
1228
|
+
"""
|
|
1229
|
+
Returns ```True``` if the object has an active workflow assigned, otherwise ```False```.
|
|
1230
|
+
"""
|
|
1231
|
+
return self.get_workflow() is not None
|
|
1232
|
+
|
|
1233
|
+
def get_workflow(self):
|
|
1234
|
+
"""
|
|
1235
|
+
Returns the active workflow assigned to the object.
|
|
1236
|
+
"""
|
|
1237
|
+
return self.get_default_workflow()
|
|
1238
|
+
|
|
1239
|
+
@property
|
|
1240
|
+
def workflow_states(self):
|
|
1241
|
+
"""
|
|
1242
|
+
Returns workflow states that belong to the object.
|
|
1243
|
+
|
|
1244
|
+
To allow filtering ``WorkflowState`` queries by the object,
|
|
1245
|
+
subclasses should define a
|
|
1246
|
+
:class:`~django.contrib.contenttypes.fields.GenericRelation` to
|
|
1247
|
+
:class:`~wagtail.models.WorkflowState` with the desired
|
|
1248
|
+
``related_query_name``. This property can be replaced with the
|
|
1249
|
+
``GenericRelation`` or overridden to allow custom logic, which can be
|
|
1250
|
+
useful if the model has inheritance.
|
|
1251
|
+
"""
|
|
1252
|
+
return WorkflowState.objects.for_instance(self)
|
|
1253
|
+
|
|
1254
|
+
@property
|
|
1255
|
+
def workflow_in_progress(self):
|
|
1256
|
+
"""
|
|
1257
|
+
Returns ```True``` if a workflow is in progress on the current object, otherwise ```False```.
|
|
1258
|
+
"""
|
|
1259
|
+
if not getattr(settings, "WAGTAIL_WORKFLOW_ENABLED", True):
|
|
1260
|
+
return False
|
|
1261
|
+
|
|
1262
|
+
# `_current_workflow_states` may be populated by `prefetch_workflow_states`
|
|
1263
|
+
# on querysets as a performance optimization
|
|
1264
|
+
if hasattr(self, "_current_workflow_states"):
|
|
1265
|
+
for state in self._current_workflow_states:
|
|
1266
|
+
if state.status == WorkflowState.STATUS_IN_PROGRESS:
|
|
1267
|
+
return True
|
|
1268
|
+
return False
|
|
1269
|
+
|
|
1270
|
+
return self.workflow_states.filter(
|
|
1271
|
+
status=WorkflowState.STATUS_IN_PROGRESS
|
|
1272
|
+
).exists()
|
|
1273
|
+
|
|
1274
|
+
@property
|
|
1275
|
+
def current_workflow_state(self):
|
|
1276
|
+
"""
|
|
1277
|
+
Returns the in progress or needs changes workflow state on this object, if it exists.
|
|
1278
|
+
"""
|
|
1279
|
+
if not getattr(settings, "WAGTAIL_WORKFLOW_ENABLED", True):
|
|
1280
|
+
return None
|
|
1281
|
+
|
|
1282
|
+
# `_current_workflow_states` may be populated by `prefetch_workflow_states`
|
|
1283
|
+
# on querysets as a performance optimization
|
|
1284
|
+
if hasattr(self, "_current_workflow_states"):
|
|
1285
|
+
try:
|
|
1286
|
+
return self._current_workflow_states[0]
|
|
1287
|
+
except IndexError:
|
|
1288
|
+
return
|
|
1289
|
+
|
|
1290
|
+
return (
|
|
1291
|
+
self.workflow_states.active()
|
|
1292
|
+
.select_related("current_task_state__task")
|
|
1293
|
+
.first()
|
|
1294
|
+
)
|
|
1295
|
+
|
|
1296
|
+
@property
|
|
1297
|
+
def current_workflow_task_state(self):
|
|
1298
|
+
"""
|
|
1299
|
+
Returns (specific class of) the current task state of the workflow on this object, if it exists.
|
|
1300
|
+
"""
|
|
1301
|
+
current_workflow_state = self.current_workflow_state
|
|
1302
|
+
if (
|
|
1303
|
+
current_workflow_state
|
|
1304
|
+
and current_workflow_state.status == WorkflowState.STATUS_IN_PROGRESS
|
|
1305
|
+
and current_workflow_state.current_task_state
|
|
1306
|
+
):
|
|
1307
|
+
return current_workflow_state.current_task_state.specific
|
|
1308
|
+
|
|
1309
|
+
@property
|
|
1310
|
+
def current_workflow_task(self):
|
|
1311
|
+
"""
|
|
1312
|
+
Returns (specific class of) the current task in progress on this object, if it exists.
|
|
1313
|
+
"""
|
|
1314
|
+
current_workflow_task_state = self.current_workflow_task_state
|
|
1315
|
+
if current_workflow_task_state:
|
|
1316
|
+
return current_workflow_task_state.task.specific
|
|
1317
|
+
|
|
1318
|
+
@property
|
|
1319
|
+
def status_string(self):
|
|
1320
|
+
if not self.live:
|
|
1321
|
+
if self.expired:
|
|
1322
|
+
return _("expired")
|
|
1323
|
+
elif self.approved_schedule:
|
|
1324
|
+
return _("scheduled")
|
|
1325
|
+
elif self.workflow_in_progress:
|
|
1326
|
+
return _("in moderation")
|
|
1327
|
+
else:
|
|
1328
|
+
return _("draft")
|
|
1329
|
+
else:
|
|
1330
|
+
if self.approved_schedule:
|
|
1331
|
+
return _("live + scheduled")
|
|
1332
|
+
elif self.workflow_in_progress:
|
|
1333
|
+
return _("live + in moderation")
|
|
1334
|
+
elif self.has_unpublished_changes:
|
|
1335
|
+
return _("live + draft")
|
|
1336
|
+
else:
|
|
1337
|
+
return _("live")
|
|
1338
|
+
|
|
1339
|
+
def get_lock(self):
|
|
1340
|
+
# Standard locking should take precedence over workflow locking
|
|
1341
|
+
# because it's possible for both to be used at the same time
|
|
1342
|
+
lock = super().get_lock()
|
|
1343
|
+
if lock:
|
|
1344
|
+
return lock
|
|
1345
|
+
|
|
1346
|
+
current_workflow_task = self.current_workflow_task
|
|
1347
|
+
if current_workflow_task:
|
|
1348
|
+
return WorkflowLock(self, current_workflow_task)
|