diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 40bbc9af65..6ae6c96a3f 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -17,6 +17,7 @@ Changelog * `update_index` management command now accepts a `--chunk_size` option to determine the number of items to load at once (Dave Bell) * Added hook `register_account_menu_item` to add new account preference items (Michael van Tellingen) * Added change email functionality from the account settings (Alejandro Garza, Alexs Mathilda) + * Add request parameter to edit handlers (Rajeev J Sebastian) * Fix: Status button on 'edit page' now links to the correct URL when live and draft slug differ (LB (Ben Johnston)) * Fix: Image title text in the gallery and in the chooser now wraps for long filenames (LB (Ben Johnston), Luiz Boaretto) * Fix: Move image editor action buttons to the bottom of the form on mobile (Julian Gallo) diff --git a/CONTRIBUTORS.rst b/CONTRIBUTORS.rst index 8d5e73e987..8dd2a46b28 100644 --- a/CONTRIBUTORS.rst +++ b/CONTRIBUTORS.rst @@ -289,6 +289,7 @@ Contributors * Mike Kamermans * Arthur Holzner * Alejandro Garza +* Rajeev J Sebastian Translators =========== diff --git a/docs/releases/2.1.rst b/docs/releases/2.1.rst index 856ea92592..358d39f80e 100644 --- a/docs/releases/2.1.rst +++ b/docs/releases/2.1.rst @@ -31,6 +31,7 @@ Other features * ``update_index`` management command now accepts a ``--chunk_size`` option to determine the number of items to load at once (Dave Bell) * Added hook `register_account_menu_item` to add new account preference items (Michael van Tellingen) * Added change email functionality from the account settings (Alejandro Garza, Alexs Mathilda) + * Add request parameter to edit handlers (Rajeev J Sebastian) Bug fixes ~~~~~~~~~ diff --git a/wagtail/admin/edit_handlers.py b/wagtail/admin/edit_handlers.py index c7440bdd2b..3103d43b5b 100644 --- a/wagtail/admin/edit_handlers.py +++ b/wagtail/admin/edit_handlers.py @@ -143,7 +143,7 @@ class EditHandler: def on_model_bound(self): pass - def bind_to_instance(self, instance=None, form=None): + def bind_to_instance(self, instance=None, form=None, request=None): new = self.bind_to_model(self.model) if not instance: @@ -154,6 +154,10 @@ class EditHandler: raise ValueError("EditHandler did not receive a form object") new.form = form + if request is None: + raise ValueError("EditHandler did not receive a request object") + new.request = request + new.on_instance_bound() return new @@ -295,7 +299,8 @@ class BaseCompositeEditHandler(EditHandler): if child.field_name not in self.form._meta.fields: continue children.append(child.bind_to_instance(instance=self.instance, - form=self.form)) + form=self.form, + request=self.request)) self.children = children def render(self): @@ -699,7 +704,8 @@ class InlinePanel(EditHandler): child_edit_handler = self.get_child_edit_handler() self.children.append( child_edit_handler.bind_to_instance(instance=subform.instance, - form=subform)) + form=subform, + request=self.request)) # if this formset is valid, it may have been re-ordered; respect that # in case the parent form errored and we need to re-render @@ -714,7 +720,7 @@ class InlinePanel(EditHandler): self.empty_child = self.get_child_edit_handler() self.empty_child = self.empty_child.bind_to_instance( - instance=empty_form.instance, form=empty_form) + instance=empty_form.instance, form=empty_form, request=self.request) template = "wagtailadmin/edit_handlers/inline_panel.html" diff --git a/wagtail/admin/tests/test_edit_handlers.py b/wagtail/admin/tests/test_edit_handlers.py index 4c1a039662..419c1dc31b 100644 --- a/wagtail/admin/tests/test_edit_handlers.py +++ b/wagtail/admin/tests/test_edit_handlers.py @@ -2,9 +2,10 @@ from datetime import date import mock from django import forms +from django.contrib.auth.models import AnonymousUser from django.core import checks from django.core.exceptions import FieldDoesNotExist, ImproperlyConfigured -from django.test import TestCase, override_settings +from django.test import RequestFactory, TestCase, override_settings from wagtail.admin.edit_handlers import ( FieldPanel, FieldRowPanel, InlinePanel, ObjectList, PageChooserPanel, RichTextFieldPanel, @@ -231,6 +232,10 @@ class TestExtractPanelDefinitionsFromModelClass(TestCase): class TestTabbedInterface(TestCase): def setUp(self): + self.request = RequestFactory().get('/') + user = AnonymousUser() # technically, Anonymous users cannot access the admin + self.request.user = user + # a custom tabbed interface for EventPage self.event_page_tabbed_interface = TabbedInterface([ ObjectList([ @@ -260,7 +265,8 @@ class TestTabbedInterface(TestCase): tabbed_interface = self.event_page_tabbed_interface.bind_to_instance( instance=event, - form=form + form=form, + request=self.request ) result = tabbed_interface.render() @@ -292,7 +298,8 @@ class TestTabbedInterface(TestCase): tabbed_interface = self.event_page_tabbed_interface.bind_to_instance( instance=event, - form=form + form=form, + request=self.request ) result = tabbed_interface.render_form_content() @@ -305,6 +312,9 @@ class TestTabbedInterface(TestCase): class TestObjectList(TestCase): def setUp(self): + self.request = RequestFactory().get('/') + user = AnonymousUser() # technically, Anonymous users cannot access the admin + self.request.user = user # a custom ObjectList for EventPage self.event_page_object_list = ObjectList([ FieldPanel('title', widget=forms.Textarea), @@ -330,7 +340,8 @@ class TestObjectList(TestCase): object_list = self.event_page_object_list.bind_to_instance( instance=event, - form=form + form=form, + request=self.request ) result = object_list.render() @@ -353,6 +364,10 @@ class TestObjectList(TestCase): class TestFieldPanel(TestCase): def setUp(self): + self.request = RequestFactory().get('/') + user = AnonymousUser() # technically, Anonymous users cannot access the admin + self.request.user = user + self.EventPageForm = get_form_for_model( EventPage, form_class=WagtailAdminPageForm, formsets=[]) self.event = EventPage(title='Abergavenny sheepdog trials', @@ -374,7 +389,8 @@ class TestFieldPanel(TestCase): field_panel = self.end_date_panel.bind_to_instance( instance=self.event, - form=form + form=form, + request=self.request ) result = field_panel.render_as_object() @@ -401,7 +417,8 @@ class TestFieldPanel(TestCase): field_panel = self.end_date_panel.bind_to_instance( instance=self.event, - form=form + form=form, + request=self.request ) result = field_panel.render_as_field() @@ -431,7 +448,8 @@ class TestFieldPanel(TestCase): field_panel = self.end_date_panel.bind_to_instance( instance=self.event, - form=form + form=form, + request=self.request ) result = field_panel.render_as_field() @@ -441,6 +459,10 @@ class TestFieldPanel(TestCase): class TestFieldRowPanel(TestCase): def setUp(self): + self.request = RequestFactory().get('/') + user = AnonymousUser() # technically, Anonymous users cannot access the admin + self.request.user = user + self.EventPageForm = get_form_for_model( EventPage, form_class=WagtailAdminPageForm, formsets=[]) self.event = EventPage(title='Abergavenny sheepdog trials', @@ -460,7 +482,8 @@ class TestFieldRowPanel(TestCase): field_panel = self.dates_panel.bind_to_instance( instance=self.event, - form=form + form=form, + request=self.request ) result = field_panel.render_as_object() @@ -479,7 +502,8 @@ class TestFieldRowPanel(TestCase): field_panel = self.dates_panel.bind_to_instance( instance=self.event, - form=form + form=form, + request=self.request ) result = field_panel.render_as_field() @@ -505,7 +529,8 @@ class TestFieldRowPanel(TestCase): field_panel = self.dates_panel.bind_to_instance( instance=self.event, - form=form + form=form, + request=self.request ) result = field_panel.render_as_field() @@ -521,7 +546,8 @@ class TestFieldRowPanel(TestCase): field_panel = self.dates_panel.bind_to_instance( instance=self.event, - form=form + form=form, + request=self.request ) result = field_panel.render_as_field() @@ -537,7 +563,8 @@ class TestFieldRowPanel(TestCase): field_panel = self.dates_panel.bind_to_instance( instance=self.event, - form=form + form=form, + request=self.request ) result = field_panel.render_as_field() @@ -547,6 +574,10 @@ class TestFieldRowPanel(TestCase): class TestFieldRowPanelWithChooser(TestCase): def setUp(self): + self.request = RequestFactory().get('/') + user = AnonymousUser() # technically, Anonymous users cannot access the admin + self.request.user = user + self.EventPageForm = get_form_for_model( EventPage, form_class=WagtailAdminPageForm, formsets=[]) self.event = EventPage(title='Abergavenny sheepdog trials', @@ -566,7 +597,8 @@ class TestFieldRowPanelWithChooser(TestCase): field_panel = self.dates_panel.bind_to_instance( instance=self.event, - form=form + form=form, + request=self.request ) result = field_panel.render_as_object() @@ -581,6 +613,10 @@ class TestPageChooserPanel(TestCase): fixtures = ['test.json'] def setUp(self): + self.request = RequestFactory().get('/') + user = AnonymousUser() # technically, Anonymous users cannot access the admin + self.request.user = user + model = PageChooserModel # a model with a foreign key to Page which we want to render as a page chooser # a PageChooserPanel class that works on PageChooserModel's 'page' field @@ -598,7 +634,7 @@ class TestPageChooserPanel(TestCase): self.form = self.PageChooserForm(instance=self.test_instance) self.page_chooser_panel = self.my_page_chooser_panel.bind_to_instance( - instance=self.test_instance, form=self.form) + instance=self.test_instance, form=self.form, request=self.request) def test_page_chooser_uses_correct_widget(self): self.assertEqual(type(self.form.fields['page'].widget), AdminPageChooser) @@ -621,7 +657,7 @@ class TestPageChooserPanel(TestCase): form = PageChooserForm(instance=self.test_instance) page_chooser_panel = my_page_chooser_panel.bind_to_instance( - instance=self.test_instance, form=form) + instance=self.test_instance, form=form, request=self.request) result = page_chooser_panel.render_as_field() # the canChooseRoot flag on createPageChooser should now be true @@ -646,7 +682,7 @@ class TestPageChooserPanel(TestCase): test_instance = PageChooserModel() form = self.PageChooserForm(instance=test_instance) page_chooser_panel = self.my_page_chooser_panel.bind_to_instance( - instance=test_instance, form=form) + instance=test_instance, form=form, request=self.request) result = page_chooser_panel.render_as_field() self.assertIn('

help text

', result) @@ -658,7 +694,7 @@ class TestPageChooserPanel(TestCase): self.assertFalse(form.is_valid()) page_chooser_panel = self.my_page_chooser_panel.bind_to_instance( - instance=self.test_instance, form=form) + instance=self.test_instance, form=form, request=self.request) self.assertIn('This field is required.', page_chooser_panel.render_as_field()) def test_override_page_type(self): @@ -671,7 +707,7 @@ class TestPageChooserPanel(TestCase): PageChooserForm = my_page_object_list.get_form_class() form = PageChooserForm(instance=self.test_instance) page_chooser_panel = my_page_chooser_panel.bind_to_instance( - instance=self.test_instance, form=form) + instance=self.test_instance, form=form, request=self.request) result = page_chooser_panel.render_as_field() expected_js = 'createPageChooser("{id}", ["{model}"], {parent}, false, null);'.format( @@ -688,7 +724,7 @@ class TestPageChooserPanel(TestCase): PageChooserForm = my_page_object_list.get_form_class() form = PageChooserForm(instance=self.test_instance) page_chooser_panel = my_page_chooser_panel.bind_to_instance( - instance=self.test_instance, form=form) + instance=self.test_instance, form=form, request=self.request) result = page_chooser_panel.render_as_field() expected_js = 'createPageChooser("{id}", ["{model}"], {parent}, false, null);'.format( @@ -723,6 +759,11 @@ class TestPageChooserPanel(TestCase): class TestInlinePanel(TestCase, WagtailTestUtils): fixtures = ['test.json'] + def setUp(self): + self.request = RequestFactory().get('/') + user = AnonymousUser() # technically, Anonymous users cannot access the admin + self.request.user = user + def test_render(self): """ Check that the inline panel renders the panels set on the model @@ -740,7 +781,8 @@ class TestInlinePanel(TestCase, WagtailTestUtils): form = EventPageForm(instance=event_page) panel = speaker_object_list.bind_to_instance(instance=event_page, - form=form) + form=form, + request=self.request) result = panel.render_as_field() @@ -795,7 +837,7 @@ class TestInlinePanel(TestCase, WagtailTestUtils): form = EventPageForm(instance=event_page) panel = speaker_inline_panel.bind_to_instance( - instance=event_page, form=form) + instance=event_page, form=form, request=self.request) result = panel.render_as_field() @@ -852,7 +894,7 @@ class TestInlinePanel(TestCase, WagtailTestUtils): event_page = EventPage.objects.get(slug='christmas') form = EventPageForm(instance=event_page) panel = speaker_inline_panel.bind_to_instance( - instance=event_page, form=form) + instance=event_page, form=form, request=self.request) self.assertIn('maxForms: 1000', panel.render_js_init()) diff --git a/wagtail/admin/views/pages.py b/wagtail/admin/views/pages.py index a5626bcbbe..9e7b59d8c9 100644 --- a/wagtail/admin/views/pages.py +++ b/wagtail/admin/views/pages.py @@ -270,12 +270,13 @@ def create(request, content_type_app_name, content_type_model_name, parent_page_ request, _("The page could not be created due to validation errors"), form ) edit_handler = edit_handler.bind_to_instance(instance=page, - form=form) + form=form, + request=request) has_unsaved_changes = True else: signals.init_new_page.send(sender=create, page=page, parent=parent_page) form = form_class(instance=page, parent_page=parent_page) - edit_handler = edit_handler.bind_to_instance(instance=page, form=form) + edit_handler = edit_handler.bind_to_instance(instance=page, form=form, request=request) has_unsaved_changes = False return render(request, 'wagtailadmin/pages/create.html', { @@ -470,7 +471,8 @@ def edit(request, page_id): ) edit_handler = edit_handler.bind_to_instance(instance=page, - form=form) + form=form, + request=request) errors_debug = ( repr(edit_handler.form.errors) + repr([ @@ -482,7 +484,7 @@ def edit(request, page_id): has_unsaved_changes = True else: form = form_class(instance=page, parent_page=parent) - edit_handler = edit_handler.bind_to_instance(instance=page, form=form) + edit_handler = edit_handler.bind_to_instance(instance=page, form=form, request=request) has_unsaved_changes = False # Check for revisions still undergoing moderation and warn @@ -1044,7 +1046,8 @@ def revisions_revert(request, page_id, revision_id): form = form_class(instance=revision_page) edit_handler = edit_handler.bind_to_instance(instance=revision_page, - form=form) + form=form, + request=request) user_avatar = render_to_string('wagtailadmin/shared/user_avatar.html', {'user': revision.user}) diff --git a/wagtail/contrib/forms/tests/test_views.py b/wagtail/contrib/forms/tests/test_views.py index 2bca7640d3..d8216201e2 100644 --- a/wagtail/contrib/forms/tests/test_views.py +++ b/wagtail/contrib/forms/tests/test_views.py @@ -2,7 +2,8 @@ import json from django.contrib.auth import get_user_model -from django.test import TestCase +from django.contrib.auth.models import AnonymousUser +from django.test import RequestFactory, TestCase from django.urls import reverse from wagtail.admin.edit_handlers import get_form_for_model @@ -20,6 +21,9 @@ from wagtail.tests.utils import WagtailTestUtils class TestFormResponsesPanel(TestCase): def setUp(self): + self.request = RequestFactory().get('/') + user = AnonymousUser() # technically, Anonymous users cannot access the admin + self.request.user = user self.form_page = make_form_page() @@ -30,7 +34,7 @@ class TestFormResponsesPanel(TestCase): submissions_panel = FormSubmissionsPanel().bind_to_model(FormPage) self.panel = submissions_panel.bind_to_instance( - instance=self.form_page, form=self.FormPageForm()) + instance=self.form_page, form=self.FormPageForm(), request=self.request) def test_render_with_submissions(self): """Show the panel with the count of submission and a link to the list_submissions view.""" @@ -56,6 +60,10 @@ class TestFormResponsesPanel(TestCase): class TestFormResponsesPanelWithCustomSubmissionClass(TestCase): def setUp(self): + self.request = RequestFactory().get('/') + user = AnonymousUser() # technically, Anonymous users cannot access the admin + self.request.user = user + # Create a form page self.form_page = make_form_page_with_custom_submission() @@ -69,7 +77,8 @@ class TestFormResponsesPanelWithCustomSubmissionClass(TestCase): submissions_panel = FormSubmissionsPanel().bind_to_model(FormPageWithCustomSubmission) self.panel = submissions_panel.bind_to_instance(self.form_page, - self.FormPageForm()) + self.FormPageForm(), + request=self.request) def test_render_with_submissions(self): """Show the panel with the count of submission and a link to the list_submissions view.""" diff --git a/wagtail/contrib/modeladmin/views.py b/wagtail/contrib/modeladmin/views.py index 16c249ac61..3f06cbc7ed 100644 --- a/wagtail/contrib/modeladmin/views.py +++ b/wagtail/contrib/modeladmin/views.py @@ -138,7 +138,7 @@ class ModelFormView(WMABaseView, FormView): edit_handler = self.get_edit_handler() form = self.get_form() edit_handler = edit_handler.bind_to_instance( - instance=instance, form=form) + instance=instance, form=form, request=self.request) context = { 'is_multipart': form.is_multipart(), 'edit_handler': edit_handler, diff --git a/wagtail/contrib/settings/views.py b/wagtail/contrib/settings/views.py index 4dcf48bf97..8e2e0e54ea 100644 --- a/wagtail/contrib/settings/views.py +++ b/wagtail/contrib/settings/views.py @@ -74,11 +74,11 @@ def edit(request, app_name, model_name, site_pk): else: messages.error(request, _("The setting could not be saved due to errors.")) edit_handler = edit_handler.bind_to_instance( - instance=instance, form=form) + instance=instance, form=form, request=request) else: form = form_class(instance=instance) edit_handler = edit_handler.bind_to_instance( - instance=instance, form=form) + instance=instance, form=form, request=request) # Show a site switcher form if there are multiple sites site_switcher = None diff --git a/wagtail/snippets/tests.py b/wagtail/snippets/tests.py index 868fd0f5ab..708a430f9e 100644 --- a/wagtail/snippets/tests.py +++ b/wagtail/snippets/tests.py @@ -1,10 +1,10 @@ from django.contrib.admin.utils import quote from django.contrib.auth import get_user_model -from django.contrib.auth.models import Permission +from django.contrib.auth.models import AnonymousUser, Permission from django.core.exceptions import ValidationError from django.core.files.base import ContentFile from django.core.files.uploadedfile import SimpleUploadedFile -from django.test import TestCase +from django.test import RequestFactory, TestCase from django.test.utils import override_settings from django.urls import reverse from taggit.models import Tag @@ -365,6 +365,10 @@ class TestSnippetChooserPanel(TestCase, WagtailTestUtils): fixtures = ['test.json'] def setUp(self): + self.request = RequestFactory().get('/') + user = AnonymousUser() # technically, Anonymous users cannot access the admin + self.request.user = user + model = SnippetChooserModel self.advert_text = 'Test advert text' test_snippet = model.objects.create( @@ -374,7 +378,8 @@ class TestSnippetChooserPanel(TestCase, WagtailTestUtils): self.form_class = self.edit_handler.get_form_class() form = self.form_class(instance=test_snippet) edit_handler = self.edit_handler.bind_to_instance(instance=test_snippet, - form=form) + form=form, + request=self.request) self.snippet_chooser_panel = [ panel for panel in edit_handler.children @@ -393,7 +398,8 @@ class TestSnippetChooserPanel(TestCase, WagtailTestUtils): test_snippet = SnippetChooserModel() form = self.form_class(instance=test_snippet) edit_handler = self.edit_handler.bind_to_instance(instance=test_snippet, - form=form) + form=form, + request=self.request) snippet_chooser_panel = [ panel for panel in edit_handler.children @@ -933,6 +939,10 @@ class TestSnippetChooserPanelWithCustomPrimaryKey(TestCase, WagtailTestUtils): fixtures = ['test.json'] def setUp(self): + self.request = RequestFactory().get('/') + user = AnonymousUser() # technically, Anonymous users cannot access the admin + self.request.user = user + model = SnippetChooserModelWithCustomPrimaryKey self.advert_text = 'Test advert text' test_snippet = model.objects.create( @@ -945,7 +955,9 @@ class TestSnippetChooserPanelWithCustomPrimaryKey(TestCase, WagtailTestUtils): self.edit_handler = get_snippet_edit_handler(model) self.form_class = self.edit_handler.get_form_class() form = self.form_class(instance=test_snippet) - edit_handler = self.edit_handler.bind_to_instance(instance=test_snippet, form=form) + edit_handler = self.edit_handler.bind_to_instance(instance=test_snippet, + form=form, + request=self.request) self.snippet_chooser_panel = [ panel for panel in edit_handler.children @@ -963,7 +975,9 @@ class TestSnippetChooserPanelWithCustomPrimaryKey(TestCase, WagtailTestUtils): def test_render_as_empty_field(self): test_snippet = SnippetChooserModelWithCustomPrimaryKey() form = self.form_class(instance=test_snippet) - edit_handler = self.edit_handler.bind_to_instance(instance=test_snippet, form=form) + edit_handler = self.edit_handler.bind_to_instance(instance=test_snippet, + form=form, + request=self.request) snippet_chooser_panel = [ panel for panel in edit_handler.children diff --git a/wagtail/snippets/views/snippets.py b/wagtail/snippets/views/snippets.py index 36a6eb9fa4..f2555e2577 100644 --- a/wagtail/snippets/views/snippets.py +++ b/wagtail/snippets/views/snippets.py @@ -153,11 +153,13 @@ def create(request, app_label, model_name): else: messages.error(request, _("The snippet could not be created due to errors.")) edit_handler = edit_handler.bind_to_instance(instance=instance, - form=form) + form=form, + request=request) else: form = form_class(instance=instance) edit_handler = edit_handler.bind_to_instance(instance=instance, - form=form) + form=form, + request=request) return render(request, 'wagtailsnippets/snippets/create.html', { 'model_opts': model._meta, @@ -199,11 +201,13 @@ def edit(request, app_label, model_name, pk): else: messages.error(request, _("The snippet could not be saved due to errors.")) edit_handler = edit_handler.bind_to_instance(instance=instance, - form=form) + form=form, + request=request) else: form = form_class(instance=instance) edit_handler = edit_handler.bind_to_instance(instance=instance, - form=form) + form=form, + request=request) return render(request, 'wagtailsnippets/snippets/edit.html', { 'model_opts': model._meta,